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     var Renderer   = function (options) {
17608       this.options = options || {};
17609     }
17610     
17611     Renderer.prototype.code = function(code, lang, escaped) {
17612       if (this.options.highlight) {
17613         var out = this.options.highlight(code, lang);
17614         if (out != null && out !== code) {
17615           escaped = true;
17616           code = out;
17617         }
17618       } else {
17619             // hack!!! - it's already escapeD?
17620             escaped = true;
17621       }
17622     
17623       if (!lang) {
17624         return '<pre><code>'
17625           + (escaped ? code : escape(code, true))
17626           + '\n</code></pre>';
17627       }
17628     
17629       return '<pre><code class="'
17630         + this.options.langPrefix
17631         + escape(lang, true)
17632         + '">'
17633         + (escaped ? code : escape(code, true))
17634         + '\n</code></pre>\n';
17635     };
17636     
17637     Renderer.prototype.blockquote = function(quote) {
17638       return '<blockquote>\n' + quote + '</blockquote>\n';
17639     };
17640     
17641     Renderer.prototype.html = function(html) {
17642       return html;
17643     };
17644     
17645     Renderer.prototype.heading = function(text, level, raw) {
17646       return '<h'
17647         + level
17648         + ' id="'
17649         + this.options.headerPrefix
17650         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17651         + '">'
17652         + text
17653         + '</h'
17654         + level
17655         + '>\n';
17656     };
17657     
17658     Renderer.prototype.hr = function() {
17659       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17660     };
17661     
17662     Renderer.prototype.list = function(body, ordered) {
17663       var type = ordered ? 'ol' : 'ul';
17664       return '<' + type + '>\n' + body + '</' + type + '>\n';
17665     };
17666     
17667     Renderer.prototype.listitem = function(text) {
17668       return '<li>' + text + '</li>\n';
17669     };
17670     
17671     Renderer.prototype.paragraph = function(text) {
17672       return '<p>' + text + '</p>\n';
17673     };
17674     
17675     Renderer.prototype.table = function(header, body) {
17676       return '<table class="table table-striped">\n'
17677         + '<thead>\n'
17678         + header
17679         + '</thead>\n'
17680         + '<tbody>\n'
17681         + body
17682         + '</tbody>\n'
17683         + '</table>\n';
17684     };
17685     
17686     Renderer.prototype.tablerow = function(content) {
17687       return '<tr>\n' + content + '</tr>\n';
17688     };
17689     
17690     Renderer.prototype.tablecell = function(content, flags) {
17691       var type = flags.header ? 'th' : 'td';
17692       var tag = flags.align
17693         ? '<' + type + ' style="text-align:' + flags.align + '">'
17694         : '<' + type + '>';
17695       return tag + content + '</' + type + '>\n';
17696     };
17697     
17698     // span level renderer
17699     Renderer.prototype.strong = function(text) {
17700       return '<strong>' + text + '</strong>';
17701     };
17702     
17703     Renderer.prototype.em = function(text) {
17704       return '<em>' + text + '</em>';
17705     };
17706     
17707     Renderer.prototype.codespan = function(text) {
17708       return '<code>' + text + '</code>';
17709     };
17710     
17711     Renderer.prototype.br = function() {
17712       return this.options.xhtml ? '<br/>' : '<br>';
17713     };
17714     
17715     Renderer.prototype.del = function(text) {
17716       return '<del>' + text + '</del>';
17717     };
17718     
17719     Renderer.prototype.link = function(href, title, text) {
17720       if (this.options.sanitize) {
17721         try {
17722           var prot = decodeURIComponent(unescape(href))
17723             .replace(/[^\w:]/g, '')
17724             .toLowerCase();
17725         } catch (e) {
17726           return '';
17727         }
17728         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17729           return '';
17730         }
17731       }
17732       var out = '<a href="' + href + '"';
17733       if (title) {
17734         out += ' title="' + title + '"';
17735       }
17736       out += '>' + text + '</a>';
17737       return out;
17738     };
17739     
17740     Renderer.prototype.image = function(href, title, text) {
17741       var out = '<img src="' + href + '" alt="' + text + '"';
17742       if (title) {
17743         out += ' title="' + title + '"';
17744       }
17745       out += this.options.xhtml ? '/>' : '>';
17746       return out;
17747     };
17748     
17749     Renderer.prototype.text = function(text) {
17750       return text;
17751     };
17752     
17753     /**
17754      * Parsing & Compiling
17755      */
17756     
17757     var Parser= function (options) {
17758       this.tokens = [];
17759       this.token = null;
17760       this.options = options || marked.defaults;
17761       this.options.renderer = this.options.renderer || new Renderer;
17762       this.renderer = this.options.renderer;
17763       this.renderer.options = this.options;
17764     }
17765     
17766     /**
17767      * Static Parse Method
17768      */
17769     
17770     Parser.parse = function(src, options, renderer) {
17771       var parser = new Parser(options, renderer);
17772       return parser.parse(src);
17773     };
17774     
17775     /**
17776      * Parse Loop
17777      */
17778     
17779     Parser.prototype.parse = function(src) {
17780       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17781       this.tokens = src.reverse();
17782     
17783       var out = '';
17784       while (this.next()) {
17785         out += this.tok();
17786       }
17787     
17788       return out;
17789     };
17790     
17791     /**
17792      * Next Token
17793      */
17794     
17795     Parser.prototype.next = function() {
17796       return this.token = this.tokens.pop();
17797     };
17798     
17799     /**
17800      * Preview Next Token
17801      */
17802     
17803     Parser.prototype.peek = function() {
17804       return this.tokens[this.tokens.length - 1] || 0;
17805     };
17806     
17807     /**
17808      * Parse Text Tokens
17809      */
17810     
17811     Parser.prototype.parseText = function() {
17812       var body = this.token.text;
17813     
17814       while (this.peek().type === 'text') {
17815         body += '\n' + this.next().text;
17816       }
17817     
17818       return this.inline.output(body);
17819     };
17820     
17821     /**
17822      * Parse Current Token
17823      */
17824     
17825     Parser.prototype.tok = function() {
17826       switch (this.token.type) {
17827         case 'space': {
17828           return '';
17829         }
17830         case 'hr': {
17831           return this.renderer.hr();
17832         }
17833         case 'heading': {
17834           return this.renderer.heading(
17835             this.inline.output(this.token.text),
17836             this.token.depth,
17837             this.token.text);
17838         }
17839         case 'code': {
17840           return this.renderer.code(this.token.text,
17841             this.token.lang,
17842             this.token.escaped);
17843         }
17844         case 'table': {
17845           var header = ''
17846             , body = ''
17847             , i
17848             , row
17849             , cell
17850             , flags
17851             , j;
17852     
17853           // header
17854           cell = '';
17855           for (i = 0; i < this.token.header.length; i++) {
17856             flags = { header: true, align: this.token.align[i] };
17857             cell += this.renderer.tablecell(
17858               this.inline.output(this.token.header[i]),
17859               { header: true, align: this.token.align[i] }
17860             );
17861           }
17862           header += this.renderer.tablerow(cell);
17863     
17864           for (i = 0; i < this.token.cells.length; i++) {
17865             row = this.token.cells[i];
17866     
17867             cell = '';
17868             for (j = 0; j < row.length; j++) {
17869               cell += this.renderer.tablecell(
17870                 this.inline.output(row[j]),
17871                 { header: false, align: this.token.align[j] }
17872               );
17873             }
17874     
17875             body += this.renderer.tablerow(cell);
17876           }
17877           return this.renderer.table(header, body);
17878         }
17879         case 'blockquote_start': {
17880           var body = '';
17881     
17882           while (this.next().type !== 'blockquote_end') {
17883             body += this.tok();
17884           }
17885     
17886           return this.renderer.blockquote(body);
17887         }
17888         case 'list_start': {
17889           var body = ''
17890             , ordered = this.token.ordered;
17891     
17892           while (this.next().type !== 'list_end') {
17893             body += this.tok();
17894           }
17895     
17896           return this.renderer.list(body, ordered);
17897         }
17898         case 'list_item_start': {
17899           var body = '';
17900     
17901           while (this.next().type !== 'list_item_end') {
17902             body += this.token.type === 'text'
17903               ? this.parseText()
17904               : this.tok();
17905           }
17906     
17907           return this.renderer.listitem(body);
17908         }
17909         case 'loose_item_start': {
17910           var body = '';
17911     
17912           while (this.next().type !== 'list_item_end') {
17913             body += this.tok();
17914           }
17915     
17916           return this.renderer.listitem(body);
17917         }
17918         case 'html': {
17919           var html = !this.token.pre && !this.options.pedantic
17920             ? this.inline.output(this.token.text)
17921             : this.token.text;
17922           return this.renderer.html(html);
17923         }
17924         case 'paragraph': {
17925           return this.renderer.paragraph(this.inline.output(this.token.text));
17926         }
17927         case 'text': {
17928           return this.renderer.paragraph(this.parseText());
17929         }
17930       }
17931     };
17932   
17933     
17934     var noop = function () {}
17935     noop.exec = noop;
17936     
17937     var merge = function (obj) {
17938       var i = 1
17939         , target
17940         , key;
17941     
17942       for (; i < arguments.length; i++) {
17943         target = arguments[i];
17944         for (key in target) {
17945           if (Object.prototype.hasOwnProperty.call(target, key)) {
17946             obj[key] = target[key];
17947           }
17948         }
17949       }
17950     
17951       return obj;
17952     }
17953     
17954     
17955     /**
17956      * Marked
17957      */
17958     
17959     var marked = function (src, opt, callback) {
17960       if (callback || typeof opt === 'function') {
17961         if (!callback) {
17962           callback = opt;
17963           opt = null;
17964         }
17965     
17966         opt = merge({}, marked.defaults, opt || {});
17967     
17968         var highlight = opt.highlight
17969           , tokens
17970           , pending
17971           , i = 0;
17972     
17973         try {
17974           tokens = Lexer.lex(src, opt)
17975         } catch (e) {
17976           return callback(e);
17977         }
17978     
17979         pending = tokens.length;
17980     
17981         var done = function(err) {
17982           if (err) {
17983             opt.highlight = highlight;
17984             return callback(err);
17985           }
17986     
17987           var out;
17988     
17989           try {
17990             out = Parser.parse(tokens, opt);
17991           } catch (e) {
17992             err = e;
17993           }
17994     
17995           opt.highlight = highlight;
17996     
17997           return err
17998             ? callback(err)
17999             : callback(null, out);
18000         };
18001     
18002         if (!highlight || highlight.length < 3) {
18003           return done();
18004         }
18005     
18006         delete opt.highlight;
18007     
18008         if (!pending) { return done(); }
18009     
18010         for (; i < tokens.length; i++) {
18011           (function(token) {
18012             if (token.type !== 'code') {
18013               return --pending || done();
18014             }
18015             return highlight(token.text, token.lang, function(err, code) {
18016               if (err) { return done(err); }
18017               if (code == null || code === token.text) {
18018                 return --pending || done();
18019               }
18020               token.text = code;
18021               token.escaped = true;
18022               --pending || done();
18023             });
18024           })(tokens[i]);
18025         }
18026     
18027         return;
18028       }
18029       try {
18030         if (opt) { opt = merge({}, marked.defaults, opt); }
18031         return Parser.parse(Lexer.lex(src, opt), opt);
18032       } catch (e) {
18033         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18034         if ((opt || marked.defaults).silent) {
18035           return '<p>An error occured:</p><pre>'
18036             + escape(e.message + '', true)
18037             + '</pre>';
18038         }
18039         throw e;
18040       }
18041     }
18042     
18043     /**
18044      * Options
18045      */
18046     
18047     marked.options =
18048     marked.setOptions = function(opt) {
18049       merge(marked.defaults, opt);
18050       return marked;
18051     };
18052     
18053     marked.defaults = {
18054       gfm: true,
18055       tables: true,
18056       breaks: false,
18057       pedantic: false,
18058       sanitize: false,
18059       sanitizer: null,
18060       mangle: true,
18061       smartLists: false,
18062       silent: false,
18063       highlight: null,
18064       langPrefix: 'lang-',
18065       smartypants: false,
18066       headerPrefix: '',
18067       renderer: new Renderer,
18068       xhtml: false
18069     };
18070     
18071     /**
18072      * Expose
18073      */
18074     
18075     marked.Parser = Parser;
18076     marked.parser = Parser.parse;
18077     
18078     marked.Renderer = Renderer;
18079     
18080     marked.Lexer = Lexer;
18081     marked.lexer = Lexer.lex;
18082     
18083     marked.InlineLexer = InlineLexer;
18084     marked.inlineLexer = InlineLexer.output;
18085     
18086     marked.parse = marked;
18087     
18088     Roo.Markdown.marked = marked;
18089
18090 })();/*
18091  * Based on:
18092  * Ext JS Library 1.1.1
18093  * Copyright(c) 2006-2007, Ext JS, LLC.
18094  *
18095  * Originally Released Under LGPL - original licence link has changed is not relivant.
18096  *
18097  * Fork - LGPL
18098  * <script type="text/javascript">
18099  */
18100
18101
18102
18103 /*
18104  * These classes are derivatives of the similarly named classes in the YUI Library.
18105  * The original license:
18106  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18107  * Code licensed under the BSD License:
18108  * http://developer.yahoo.net/yui/license.txt
18109  */
18110
18111 (function() {
18112
18113 var Event=Roo.EventManager;
18114 var Dom=Roo.lib.Dom;
18115
18116 /**
18117  * @class Roo.dd.DragDrop
18118  * @extends Roo.util.Observable
18119  * Defines the interface and base operation of items that that can be
18120  * dragged or can be drop targets.  It was designed to be extended, overriding
18121  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18122  * Up to three html elements can be associated with a DragDrop instance:
18123  * <ul>
18124  * <li>linked element: the element that is passed into the constructor.
18125  * This is the element which defines the boundaries for interaction with
18126  * other DragDrop objects.</li>
18127  * <li>handle element(s): The drag operation only occurs if the element that
18128  * was clicked matches a handle element.  By default this is the linked
18129  * element, but there are times that you will want only a portion of the
18130  * linked element to initiate the drag operation, and the setHandleElId()
18131  * method provides a way to define this.</li>
18132  * <li>drag element: this represents the element that would be moved along
18133  * with the cursor during a drag operation.  By default, this is the linked
18134  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18135  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18136  * </li>
18137  * </ul>
18138  * This class should not be instantiated until the onload event to ensure that
18139  * the associated elements are available.
18140  * The following would define a DragDrop obj that would interact with any
18141  * other DragDrop obj in the "group1" group:
18142  * <pre>
18143  *  dd = new Roo.dd.DragDrop("div1", "group1");
18144  * </pre>
18145  * Since none of the event handlers have been implemented, nothing would
18146  * actually happen if you were to run the code above.  Normally you would
18147  * override this class or one of the default implementations, but you can
18148  * also override the methods you want on an instance of the class...
18149  * <pre>
18150  *  dd.onDragDrop = function(e, id) {
18151  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18152  *  }
18153  * </pre>
18154  * @constructor
18155  * @param {String} id of the element that is linked to this instance
18156  * @param {String} sGroup the group of related DragDrop objects
18157  * @param {object} config an object containing configurable attributes
18158  *                Valid properties for DragDrop:
18159  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18160  */
18161 Roo.dd.DragDrop = function(id, sGroup, config) {
18162     if (id) {
18163         this.init(id, sGroup, config);
18164     }
18165     
18166 };
18167
18168 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18169
18170     /**
18171      * The id of the element associated with this object.  This is what we
18172      * refer to as the "linked element" because the size and position of
18173      * this element is used to determine when the drag and drop objects have
18174      * interacted.
18175      * @property id
18176      * @type String
18177      */
18178     id: null,
18179
18180     /**
18181      * Configuration attributes passed into the constructor
18182      * @property config
18183      * @type object
18184      */
18185     config: null,
18186
18187     /**
18188      * The id of the element that will be dragged.  By default this is same
18189      * as the linked element , but could be changed to another element. Ex:
18190      * Roo.dd.DDProxy
18191      * @property dragElId
18192      * @type String
18193      * @private
18194      */
18195     dragElId: null,
18196
18197     /**
18198      * the id of the element that initiates the drag operation.  By default
18199      * this is the linked element, but could be changed to be a child of this
18200      * element.  This lets us do things like only starting the drag when the
18201      * header element within the linked html element is clicked.
18202      * @property handleElId
18203      * @type String
18204      * @private
18205      */
18206     handleElId: null,
18207
18208     /**
18209      * An associative array of HTML tags that will be ignored if clicked.
18210      * @property invalidHandleTypes
18211      * @type {string: string}
18212      */
18213     invalidHandleTypes: null,
18214
18215     /**
18216      * An associative array of ids for elements that will be ignored if clicked
18217      * @property invalidHandleIds
18218      * @type {string: string}
18219      */
18220     invalidHandleIds: null,
18221
18222     /**
18223      * An indexted array of css class names for elements that will be ignored
18224      * if clicked.
18225      * @property invalidHandleClasses
18226      * @type string[]
18227      */
18228     invalidHandleClasses: null,
18229
18230     /**
18231      * The linked element's absolute X position at the time the drag was
18232      * started
18233      * @property startPageX
18234      * @type int
18235      * @private
18236      */
18237     startPageX: 0,
18238
18239     /**
18240      * The linked element's absolute X position at the time the drag was
18241      * started
18242      * @property startPageY
18243      * @type int
18244      * @private
18245      */
18246     startPageY: 0,
18247
18248     /**
18249      * The group defines a logical collection of DragDrop objects that are
18250      * related.  Instances only get events when interacting with other
18251      * DragDrop object in the same group.  This lets us define multiple
18252      * groups using a single DragDrop subclass if we want.
18253      * @property groups
18254      * @type {string: string}
18255      */
18256     groups: null,
18257
18258     /**
18259      * Individual drag/drop instances can be locked.  This will prevent
18260      * onmousedown start drag.
18261      * @property locked
18262      * @type boolean
18263      * @private
18264      */
18265     locked: false,
18266
18267     /**
18268      * Lock this instance
18269      * @method lock
18270      */
18271     lock: function() { this.locked = true; },
18272
18273     /**
18274      * Unlock this instace
18275      * @method unlock
18276      */
18277     unlock: function() { this.locked = false; },
18278
18279     /**
18280      * By default, all insances can be a drop target.  This can be disabled by
18281      * setting isTarget to false.
18282      * @method isTarget
18283      * @type boolean
18284      */
18285     isTarget: true,
18286
18287     /**
18288      * The padding configured for this drag and drop object for calculating
18289      * the drop zone intersection with this object.
18290      * @method padding
18291      * @type int[]
18292      */
18293     padding: null,
18294
18295     /**
18296      * Cached reference to the linked element
18297      * @property _domRef
18298      * @private
18299      */
18300     _domRef: null,
18301
18302     /**
18303      * Internal typeof flag
18304      * @property __ygDragDrop
18305      * @private
18306      */
18307     __ygDragDrop: true,
18308
18309     /**
18310      * Set to true when horizontal contraints are applied
18311      * @property constrainX
18312      * @type boolean
18313      * @private
18314      */
18315     constrainX: false,
18316
18317     /**
18318      * Set to true when vertical contraints are applied
18319      * @property constrainY
18320      * @type boolean
18321      * @private
18322      */
18323     constrainY: false,
18324
18325     /**
18326      * The left constraint
18327      * @property minX
18328      * @type int
18329      * @private
18330      */
18331     minX: 0,
18332
18333     /**
18334      * The right constraint
18335      * @property maxX
18336      * @type int
18337      * @private
18338      */
18339     maxX: 0,
18340
18341     /**
18342      * The up constraint
18343      * @property minY
18344      * @type int
18345      * @type int
18346      * @private
18347      */
18348     minY: 0,
18349
18350     /**
18351      * The down constraint
18352      * @property maxY
18353      * @type int
18354      * @private
18355      */
18356     maxY: 0,
18357
18358     /**
18359      * Maintain offsets when we resetconstraints.  Set to true when you want
18360      * the position of the element relative to its parent to stay the same
18361      * when the page changes
18362      *
18363      * @property maintainOffset
18364      * @type boolean
18365      */
18366     maintainOffset: false,
18367
18368     /**
18369      * Array of pixel locations the element will snap to if we specified a
18370      * horizontal graduation/interval.  This array is generated automatically
18371      * when you define a tick interval.
18372      * @property xTicks
18373      * @type int[]
18374      */
18375     xTicks: null,
18376
18377     /**
18378      * Array of pixel locations the element will snap to if we specified a
18379      * vertical graduation/interval.  This array is generated automatically
18380      * when you define a tick interval.
18381      * @property yTicks
18382      * @type int[]
18383      */
18384     yTicks: null,
18385
18386     /**
18387      * By default the drag and drop instance will only respond to the primary
18388      * button click (left button for a right-handed mouse).  Set to true to
18389      * allow drag and drop to start with any mouse click that is propogated
18390      * by the browser
18391      * @property primaryButtonOnly
18392      * @type boolean
18393      */
18394     primaryButtonOnly: true,
18395
18396     /**
18397      * The availabe property is false until the linked dom element is accessible.
18398      * @property available
18399      * @type boolean
18400      */
18401     available: false,
18402
18403     /**
18404      * By default, drags can only be initiated if the mousedown occurs in the
18405      * region the linked element is.  This is done in part to work around a
18406      * bug in some browsers that mis-report the mousedown if the previous
18407      * mouseup happened outside of the window.  This property is set to true
18408      * if outer handles are defined.
18409      *
18410      * @property hasOuterHandles
18411      * @type boolean
18412      * @default false
18413      */
18414     hasOuterHandles: false,
18415
18416     /**
18417      * Code that executes immediately before the startDrag event
18418      * @method b4StartDrag
18419      * @private
18420      */
18421     b4StartDrag: function(x, y) { },
18422
18423     /**
18424      * Abstract method called after a drag/drop object is clicked
18425      * and the drag or mousedown time thresholds have beeen met.
18426      * @method startDrag
18427      * @param {int} X click location
18428      * @param {int} Y click location
18429      */
18430     startDrag: function(x, y) { /* override this */ },
18431
18432     /**
18433      * Code that executes immediately before the onDrag event
18434      * @method b4Drag
18435      * @private
18436      */
18437     b4Drag: function(e) { },
18438
18439     /**
18440      * Abstract method called during the onMouseMove event while dragging an
18441      * object.
18442      * @method onDrag
18443      * @param {Event} e the mousemove event
18444      */
18445     onDrag: function(e) { /* override this */ },
18446
18447     /**
18448      * Abstract method called when this element fist begins hovering over
18449      * another DragDrop obj
18450      * @method onDragEnter
18451      * @param {Event} e the mousemove event
18452      * @param {String|DragDrop[]} id In POINT mode, the element
18453      * id this is hovering over.  In INTERSECT mode, an array of one or more
18454      * dragdrop items being hovered over.
18455      */
18456     onDragEnter: function(e, id) { /* override this */ },
18457
18458     /**
18459      * Code that executes immediately before the onDragOver event
18460      * @method b4DragOver
18461      * @private
18462      */
18463     b4DragOver: function(e) { },
18464
18465     /**
18466      * Abstract method called when this element is hovering over another
18467      * DragDrop obj
18468      * @method onDragOver
18469      * @param {Event} e the mousemove event
18470      * @param {String|DragDrop[]} id In POINT mode, the element
18471      * id this is hovering over.  In INTERSECT mode, an array of dd items
18472      * being hovered over.
18473      */
18474     onDragOver: function(e, id) { /* override this */ },
18475
18476     /**
18477      * Code that executes immediately before the onDragOut event
18478      * @method b4DragOut
18479      * @private
18480      */
18481     b4DragOut: function(e) { },
18482
18483     /**
18484      * Abstract method called when we are no longer hovering over an element
18485      * @method onDragOut
18486      * @param {Event} e the mousemove event
18487      * @param {String|DragDrop[]} id In POINT mode, the element
18488      * id this was hovering over.  In INTERSECT mode, an array of dd items
18489      * that the mouse is no longer over.
18490      */
18491     onDragOut: function(e, id) { /* override this */ },
18492
18493     /**
18494      * Code that executes immediately before the onDragDrop event
18495      * @method b4DragDrop
18496      * @private
18497      */
18498     b4DragDrop: function(e) { },
18499
18500     /**
18501      * Abstract method called when this item is dropped on another DragDrop
18502      * obj
18503      * @method onDragDrop
18504      * @param {Event} e the mouseup event
18505      * @param {String|DragDrop[]} id In POINT mode, the element
18506      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18507      * was dropped on.
18508      */
18509     onDragDrop: function(e, id) { /* override this */ },
18510
18511     /**
18512      * Abstract method called when this item is dropped on an area with no
18513      * drop target
18514      * @method onInvalidDrop
18515      * @param {Event} e the mouseup event
18516      */
18517     onInvalidDrop: function(e) { /* override this */ },
18518
18519     /**
18520      * Code that executes immediately before the endDrag event
18521      * @method b4EndDrag
18522      * @private
18523      */
18524     b4EndDrag: function(e) { },
18525
18526     /**
18527      * Fired when we are done dragging the object
18528      * @method endDrag
18529      * @param {Event} e the mouseup event
18530      */
18531     endDrag: function(e) { /* override this */ },
18532
18533     /**
18534      * Code executed immediately before the onMouseDown event
18535      * @method b4MouseDown
18536      * @param {Event} e the mousedown event
18537      * @private
18538      */
18539     b4MouseDown: function(e) {  },
18540
18541     /**
18542      * Event handler that fires when a drag/drop obj gets a mousedown
18543      * @method onMouseDown
18544      * @param {Event} e the mousedown event
18545      */
18546     onMouseDown: function(e) { /* override this */ },
18547
18548     /**
18549      * Event handler that fires when a drag/drop obj gets a mouseup
18550      * @method onMouseUp
18551      * @param {Event} e the mouseup event
18552      */
18553     onMouseUp: function(e) { /* override this */ },
18554
18555     /**
18556      * Override the onAvailable method to do what is needed after the initial
18557      * position was determined.
18558      * @method onAvailable
18559      */
18560     onAvailable: function () {
18561     },
18562
18563     /*
18564      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18565      * @type Object
18566      */
18567     defaultPadding : {left:0, right:0, top:0, bottom:0},
18568
18569     /*
18570      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18571  *
18572  * Usage:
18573  <pre><code>
18574  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18575                 { dragElId: "existingProxyDiv" });
18576  dd.startDrag = function(){
18577      this.constrainTo("parent-id");
18578  };
18579  </code></pre>
18580  * Or you can initalize it using the {@link Roo.Element} object:
18581  <pre><code>
18582  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18583      startDrag : function(){
18584          this.constrainTo("parent-id");
18585      }
18586  });
18587  </code></pre>
18588      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18589      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18590      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18591      * an object containing the sides to pad. For example: {right:10, bottom:10}
18592      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18593      */
18594     constrainTo : function(constrainTo, pad, inContent){
18595         if(typeof pad == "number"){
18596             pad = {left: pad, right:pad, top:pad, bottom:pad};
18597         }
18598         pad = pad || this.defaultPadding;
18599         var b = Roo.get(this.getEl()).getBox();
18600         var ce = Roo.get(constrainTo);
18601         var s = ce.getScroll();
18602         var c, cd = ce.dom;
18603         if(cd == document.body){
18604             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18605         }else{
18606             xy = ce.getXY();
18607             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18608         }
18609
18610
18611         var topSpace = b.y - c.y;
18612         var leftSpace = b.x - c.x;
18613
18614         this.resetConstraints();
18615         this.setXConstraint(leftSpace - (pad.left||0), // left
18616                 c.width - leftSpace - b.width - (pad.right||0) //right
18617         );
18618         this.setYConstraint(topSpace - (pad.top||0), //top
18619                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18620         );
18621     },
18622
18623     /**
18624      * Returns a reference to the linked element
18625      * @method getEl
18626      * @return {HTMLElement} the html element
18627      */
18628     getEl: function() {
18629         if (!this._domRef) {
18630             this._domRef = Roo.getDom(this.id);
18631         }
18632
18633         return this._domRef;
18634     },
18635
18636     /**
18637      * Returns a reference to the actual element to drag.  By default this is
18638      * the same as the html element, but it can be assigned to another
18639      * element. An example of this can be found in Roo.dd.DDProxy
18640      * @method getDragEl
18641      * @return {HTMLElement} the html element
18642      */
18643     getDragEl: function() {
18644         return Roo.getDom(this.dragElId);
18645     },
18646
18647     /**
18648      * Sets up the DragDrop object.  Must be called in the constructor of any
18649      * Roo.dd.DragDrop subclass
18650      * @method init
18651      * @param id the id of the linked element
18652      * @param {String} sGroup the group of related items
18653      * @param {object} config configuration attributes
18654      */
18655     init: function(id, sGroup, config) {
18656         this.initTarget(id, sGroup, config);
18657         if (!Roo.isTouch) {
18658             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18659         }
18660         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18661         // Event.on(this.id, "selectstart", Event.preventDefault);
18662     },
18663
18664     /**
18665      * Initializes Targeting functionality only... the object does not
18666      * get a mousedown handler.
18667      * @method initTarget
18668      * @param id the id of the linked element
18669      * @param {String} sGroup the group of related items
18670      * @param {object} config configuration attributes
18671      */
18672     initTarget: function(id, sGroup, config) {
18673
18674         // configuration attributes
18675         this.config = config || {};
18676
18677         // create a local reference to the drag and drop manager
18678         this.DDM = Roo.dd.DDM;
18679         // initialize the groups array
18680         this.groups = {};
18681
18682         // assume that we have an element reference instead of an id if the
18683         // parameter is not a string
18684         if (typeof id !== "string") {
18685             id = Roo.id(id);
18686         }
18687
18688         // set the id
18689         this.id = id;
18690
18691         // add to an interaction group
18692         this.addToGroup((sGroup) ? sGroup : "default");
18693
18694         // We don't want to register this as the handle with the manager
18695         // so we just set the id rather than calling the setter.
18696         this.handleElId = id;
18697
18698         // the linked element is the element that gets dragged by default
18699         this.setDragElId(id);
18700
18701         // by default, clicked anchors will not start drag operations.
18702         this.invalidHandleTypes = { A: "A" };
18703         this.invalidHandleIds = {};
18704         this.invalidHandleClasses = [];
18705
18706         this.applyConfig();
18707
18708         this.handleOnAvailable();
18709     },
18710
18711     /**
18712      * Applies the configuration parameters that were passed into the constructor.
18713      * This is supposed to happen at each level through the inheritance chain.  So
18714      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18715      * DragDrop in order to get all of the parameters that are available in
18716      * each object.
18717      * @method applyConfig
18718      */
18719     applyConfig: function() {
18720
18721         // configurable properties:
18722         //    padding, isTarget, maintainOffset, primaryButtonOnly
18723         this.padding           = this.config.padding || [0, 0, 0, 0];
18724         this.isTarget          = (this.config.isTarget !== false);
18725         this.maintainOffset    = (this.config.maintainOffset);
18726         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18727
18728     },
18729
18730     /**
18731      * Executed when the linked element is available
18732      * @method handleOnAvailable
18733      * @private
18734      */
18735     handleOnAvailable: function() {
18736         this.available = true;
18737         this.resetConstraints();
18738         this.onAvailable();
18739     },
18740
18741      /**
18742      * Configures the padding for the target zone in px.  Effectively expands
18743      * (or reduces) the virtual object size for targeting calculations.
18744      * Supports css-style shorthand; if only one parameter is passed, all sides
18745      * will have that padding, and if only two are passed, the top and bottom
18746      * will have the first param, the left and right the second.
18747      * @method setPadding
18748      * @param {int} iTop    Top pad
18749      * @param {int} iRight  Right pad
18750      * @param {int} iBot    Bot pad
18751      * @param {int} iLeft   Left pad
18752      */
18753     setPadding: function(iTop, iRight, iBot, iLeft) {
18754         // this.padding = [iLeft, iRight, iTop, iBot];
18755         if (!iRight && 0 !== iRight) {
18756             this.padding = [iTop, iTop, iTop, iTop];
18757         } else if (!iBot && 0 !== iBot) {
18758             this.padding = [iTop, iRight, iTop, iRight];
18759         } else {
18760             this.padding = [iTop, iRight, iBot, iLeft];
18761         }
18762     },
18763
18764     /**
18765      * Stores the initial placement of the linked element.
18766      * @method setInitialPosition
18767      * @param {int} diffX   the X offset, default 0
18768      * @param {int} diffY   the Y offset, default 0
18769      */
18770     setInitPosition: function(diffX, diffY) {
18771         var el = this.getEl();
18772
18773         if (!this.DDM.verifyEl(el)) {
18774             return;
18775         }
18776
18777         var dx = diffX || 0;
18778         var dy = diffY || 0;
18779
18780         var p = Dom.getXY( el );
18781
18782         this.initPageX = p[0] - dx;
18783         this.initPageY = p[1] - dy;
18784
18785         this.lastPageX = p[0];
18786         this.lastPageY = p[1];
18787
18788
18789         this.setStartPosition(p);
18790     },
18791
18792     /**
18793      * Sets the start position of the element.  This is set when the obj
18794      * is initialized, the reset when a drag is started.
18795      * @method setStartPosition
18796      * @param pos current position (from previous lookup)
18797      * @private
18798      */
18799     setStartPosition: function(pos) {
18800         var p = pos || Dom.getXY( this.getEl() );
18801         this.deltaSetXY = null;
18802
18803         this.startPageX = p[0];
18804         this.startPageY = p[1];
18805     },
18806
18807     /**
18808      * Add this instance to a group of related drag/drop objects.  All
18809      * instances belong to at least one group, and can belong to as many
18810      * groups as needed.
18811      * @method addToGroup
18812      * @param sGroup {string} the name of the group
18813      */
18814     addToGroup: function(sGroup) {
18815         this.groups[sGroup] = true;
18816         this.DDM.regDragDrop(this, sGroup);
18817     },
18818
18819     /**
18820      * Remove's this instance from the supplied interaction group
18821      * @method removeFromGroup
18822      * @param {string}  sGroup  The group to drop
18823      */
18824     removeFromGroup: function(sGroup) {
18825         if (this.groups[sGroup]) {
18826             delete this.groups[sGroup];
18827         }
18828
18829         this.DDM.removeDDFromGroup(this, sGroup);
18830     },
18831
18832     /**
18833      * Allows you to specify that an element other than the linked element
18834      * will be moved with the cursor during a drag
18835      * @method setDragElId
18836      * @param id {string} the id of the element that will be used to initiate the drag
18837      */
18838     setDragElId: function(id) {
18839         this.dragElId = id;
18840     },
18841
18842     /**
18843      * Allows you to specify a child of the linked element that should be
18844      * used to initiate the drag operation.  An example of this would be if
18845      * you have a content div with text and links.  Clicking anywhere in the
18846      * content area would normally start the drag operation.  Use this method
18847      * to specify that an element inside of the content div is the element
18848      * that starts the drag operation.
18849      * @method setHandleElId
18850      * @param id {string} the id of the element that will be used to
18851      * initiate the drag.
18852      */
18853     setHandleElId: function(id) {
18854         if (typeof id !== "string") {
18855             id = Roo.id(id);
18856         }
18857         this.handleElId = id;
18858         this.DDM.regHandle(this.id, id);
18859     },
18860
18861     /**
18862      * Allows you to set an element outside of the linked element as a drag
18863      * handle
18864      * @method setOuterHandleElId
18865      * @param id the id of the element that will be used to initiate the drag
18866      */
18867     setOuterHandleElId: function(id) {
18868         if (typeof id !== "string") {
18869             id = Roo.id(id);
18870         }
18871         Event.on(id, "mousedown",
18872                 this.handleMouseDown, this);
18873         this.setHandleElId(id);
18874
18875         this.hasOuterHandles = true;
18876     },
18877
18878     /**
18879      * Remove all drag and drop hooks for this element
18880      * @method unreg
18881      */
18882     unreg: function() {
18883         Event.un(this.id, "mousedown",
18884                 this.handleMouseDown);
18885         Event.un(this.id, "touchstart",
18886                 this.handleMouseDown);
18887         this._domRef = null;
18888         this.DDM._remove(this);
18889     },
18890
18891     destroy : function(){
18892         this.unreg();
18893     },
18894
18895     /**
18896      * Returns true if this instance is locked, or the drag drop mgr is locked
18897      * (meaning that all drag/drop is disabled on the page.)
18898      * @method isLocked
18899      * @return {boolean} true if this obj or all drag/drop is locked, else
18900      * false
18901      */
18902     isLocked: function() {
18903         return (this.DDM.isLocked() || this.locked);
18904     },
18905
18906     /**
18907      * Fired when this object is clicked
18908      * @method handleMouseDown
18909      * @param {Event} e
18910      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18911      * @private
18912      */
18913     handleMouseDown: function(e, oDD){
18914      
18915         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18916             //Roo.log('not touch/ button !=0');
18917             return;
18918         }
18919         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18920             return; // double touch..
18921         }
18922         
18923
18924         if (this.isLocked()) {
18925             //Roo.log('locked');
18926             return;
18927         }
18928
18929         this.DDM.refreshCache(this.groups);
18930 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18931         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18932         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18933             //Roo.log('no outer handes or not over target');
18934                 // do nothing.
18935         } else {
18936 //            Roo.log('check validator');
18937             if (this.clickValidator(e)) {
18938 //                Roo.log('validate success');
18939                 // set the initial element position
18940                 this.setStartPosition();
18941
18942
18943                 this.b4MouseDown(e);
18944                 this.onMouseDown(e);
18945
18946                 this.DDM.handleMouseDown(e, this);
18947
18948                 this.DDM.stopEvent(e);
18949             } else {
18950
18951
18952             }
18953         }
18954     },
18955
18956     clickValidator: function(e) {
18957         var target = e.getTarget();
18958         return ( this.isValidHandleChild(target) &&
18959                     (this.id == this.handleElId ||
18960                         this.DDM.handleWasClicked(target, this.id)) );
18961     },
18962
18963     /**
18964      * Allows you to specify a tag name that should not start a drag operation
18965      * when clicked.  This is designed to facilitate embedding links within a
18966      * drag handle that do something other than start the drag.
18967      * @method addInvalidHandleType
18968      * @param {string} tagName the type of element to exclude
18969      */
18970     addInvalidHandleType: function(tagName) {
18971         var type = tagName.toUpperCase();
18972         this.invalidHandleTypes[type] = type;
18973     },
18974
18975     /**
18976      * Lets you to specify an element id for a child of a drag handle
18977      * that should not initiate a drag
18978      * @method addInvalidHandleId
18979      * @param {string} id the element id of the element you wish to ignore
18980      */
18981     addInvalidHandleId: function(id) {
18982         if (typeof id !== "string") {
18983             id = Roo.id(id);
18984         }
18985         this.invalidHandleIds[id] = id;
18986     },
18987
18988     /**
18989      * Lets you specify a css class of elements that will not initiate a drag
18990      * @method addInvalidHandleClass
18991      * @param {string} cssClass the class of the elements you wish to ignore
18992      */
18993     addInvalidHandleClass: function(cssClass) {
18994         this.invalidHandleClasses.push(cssClass);
18995     },
18996
18997     /**
18998      * Unsets an excluded tag name set by addInvalidHandleType
18999      * @method removeInvalidHandleType
19000      * @param {string} tagName the type of element to unexclude
19001      */
19002     removeInvalidHandleType: function(tagName) {
19003         var type = tagName.toUpperCase();
19004         // this.invalidHandleTypes[type] = null;
19005         delete this.invalidHandleTypes[type];
19006     },
19007
19008     /**
19009      * Unsets an invalid handle id
19010      * @method removeInvalidHandleId
19011      * @param {string} id the id of the element to re-enable
19012      */
19013     removeInvalidHandleId: function(id) {
19014         if (typeof id !== "string") {
19015             id = Roo.id(id);
19016         }
19017         delete this.invalidHandleIds[id];
19018     },
19019
19020     /**
19021      * Unsets an invalid css class
19022      * @method removeInvalidHandleClass
19023      * @param {string} cssClass the class of the element(s) you wish to
19024      * re-enable
19025      */
19026     removeInvalidHandleClass: function(cssClass) {
19027         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19028             if (this.invalidHandleClasses[i] == cssClass) {
19029                 delete this.invalidHandleClasses[i];
19030             }
19031         }
19032     },
19033
19034     /**
19035      * Checks the tag exclusion list to see if this click should be ignored
19036      * @method isValidHandleChild
19037      * @param {HTMLElement} node the HTMLElement to evaluate
19038      * @return {boolean} true if this is a valid tag type, false if not
19039      */
19040     isValidHandleChild: function(node) {
19041
19042         var valid = true;
19043         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19044         var nodeName;
19045         try {
19046             nodeName = node.nodeName.toUpperCase();
19047         } catch(e) {
19048             nodeName = node.nodeName;
19049         }
19050         valid = valid && !this.invalidHandleTypes[nodeName];
19051         valid = valid && !this.invalidHandleIds[node.id];
19052
19053         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19054             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19055         }
19056
19057
19058         return valid;
19059
19060     },
19061
19062     /**
19063      * Create the array of horizontal tick marks if an interval was specified
19064      * in setXConstraint().
19065      * @method setXTicks
19066      * @private
19067      */
19068     setXTicks: function(iStartX, iTickSize) {
19069         this.xTicks = [];
19070         this.xTickSize = iTickSize;
19071
19072         var tickMap = {};
19073
19074         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19075             if (!tickMap[i]) {
19076                 this.xTicks[this.xTicks.length] = i;
19077                 tickMap[i] = true;
19078             }
19079         }
19080
19081         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19082             if (!tickMap[i]) {
19083                 this.xTicks[this.xTicks.length] = i;
19084                 tickMap[i] = true;
19085             }
19086         }
19087
19088         this.xTicks.sort(this.DDM.numericSort) ;
19089     },
19090
19091     /**
19092      * Create the array of vertical tick marks if an interval was specified in
19093      * setYConstraint().
19094      * @method setYTicks
19095      * @private
19096      */
19097     setYTicks: function(iStartY, iTickSize) {
19098         this.yTicks = [];
19099         this.yTickSize = iTickSize;
19100
19101         var tickMap = {};
19102
19103         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19104             if (!tickMap[i]) {
19105                 this.yTicks[this.yTicks.length] = i;
19106                 tickMap[i] = true;
19107             }
19108         }
19109
19110         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19111             if (!tickMap[i]) {
19112                 this.yTicks[this.yTicks.length] = i;
19113                 tickMap[i] = true;
19114             }
19115         }
19116
19117         this.yTicks.sort(this.DDM.numericSort) ;
19118     },
19119
19120     /**
19121      * By default, the element can be dragged any place on the screen.  Use
19122      * this method to limit the horizontal travel of the element.  Pass in
19123      * 0,0 for the parameters if you want to lock the drag to the y axis.
19124      * @method setXConstraint
19125      * @param {int} iLeft the number of pixels the element can move to the left
19126      * @param {int} iRight the number of pixels the element can move to the
19127      * right
19128      * @param {int} iTickSize optional parameter for specifying that the
19129      * element
19130      * should move iTickSize pixels at a time.
19131      */
19132     setXConstraint: function(iLeft, iRight, iTickSize) {
19133         this.leftConstraint = iLeft;
19134         this.rightConstraint = iRight;
19135
19136         this.minX = this.initPageX - iLeft;
19137         this.maxX = this.initPageX + iRight;
19138         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19139
19140         this.constrainX = true;
19141     },
19142
19143     /**
19144      * Clears any constraints applied to this instance.  Also clears ticks
19145      * since they can't exist independent of a constraint at this time.
19146      * @method clearConstraints
19147      */
19148     clearConstraints: function() {
19149         this.constrainX = false;
19150         this.constrainY = false;
19151         this.clearTicks();
19152     },
19153
19154     /**
19155      * Clears any tick interval defined for this instance
19156      * @method clearTicks
19157      */
19158     clearTicks: function() {
19159         this.xTicks = null;
19160         this.yTicks = null;
19161         this.xTickSize = 0;
19162         this.yTickSize = 0;
19163     },
19164
19165     /**
19166      * By default, the element can be dragged any place on the screen.  Set
19167      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19168      * parameters if you want to lock the drag to the x axis.
19169      * @method setYConstraint
19170      * @param {int} iUp the number of pixels the element can move up
19171      * @param {int} iDown the number of pixels the element can move down
19172      * @param {int} iTickSize optional parameter for specifying that the
19173      * element should move iTickSize pixels at a time.
19174      */
19175     setYConstraint: function(iUp, iDown, iTickSize) {
19176         this.topConstraint = iUp;
19177         this.bottomConstraint = iDown;
19178
19179         this.minY = this.initPageY - iUp;
19180         this.maxY = this.initPageY + iDown;
19181         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19182
19183         this.constrainY = true;
19184
19185     },
19186
19187     /**
19188      * resetConstraints must be called if you manually reposition a dd element.
19189      * @method resetConstraints
19190      * @param {boolean} maintainOffset
19191      */
19192     resetConstraints: function() {
19193
19194
19195         // Maintain offsets if necessary
19196         if (this.initPageX || this.initPageX === 0) {
19197             // figure out how much this thing has moved
19198             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19199             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19200
19201             this.setInitPosition(dx, dy);
19202
19203         // This is the first time we have detected the element's position
19204         } else {
19205             this.setInitPosition();
19206         }
19207
19208         if (this.constrainX) {
19209             this.setXConstraint( this.leftConstraint,
19210                                  this.rightConstraint,
19211                                  this.xTickSize        );
19212         }
19213
19214         if (this.constrainY) {
19215             this.setYConstraint( this.topConstraint,
19216                                  this.bottomConstraint,
19217                                  this.yTickSize         );
19218         }
19219     },
19220
19221     /**
19222      * Normally the drag element is moved pixel by pixel, but we can specify
19223      * that it move a number of pixels at a time.  This method resolves the
19224      * location when we have it set up like this.
19225      * @method getTick
19226      * @param {int} val where we want to place the object
19227      * @param {int[]} tickArray sorted array of valid points
19228      * @return {int} the closest tick
19229      * @private
19230      */
19231     getTick: function(val, tickArray) {
19232
19233         if (!tickArray) {
19234             // If tick interval is not defined, it is effectively 1 pixel,
19235             // so we return the value passed to us.
19236             return val;
19237         } else if (tickArray[0] >= val) {
19238             // The value is lower than the first tick, so we return the first
19239             // tick.
19240             return tickArray[0];
19241         } else {
19242             for (var i=0, len=tickArray.length; i<len; ++i) {
19243                 var next = i + 1;
19244                 if (tickArray[next] && tickArray[next] >= val) {
19245                     var diff1 = val - tickArray[i];
19246                     var diff2 = tickArray[next] - val;
19247                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19248                 }
19249             }
19250
19251             // The value is larger than the last tick, so we return the last
19252             // tick.
19253             return tickArray[tickArray.length - 1];
19254         }
19255     },
19256
19257     /**
19258      * toString method
19259      * @method toString
19260      * @return {string} string representation of the dd obj
19261      */
19262     toString: function() {
19263         return ("DragDrop " + this.id);
19264     }
19265
19266 });
19267
19268 })();
19269 /*
19270  * Based on:
19271  * Ext JS Library 1.1.1
19272  * Copyright(c) 2006-2007, Ext JS, LLC.
19273  *
19274  * Originally Released Under LGPL - original licence link has changed is not relivant.
19275  *
19276  * Fork - LGPL
19277  * <script type="text/javascript">
19278  */
19279
19280
19281 /**
19282  * The drag and drop utility provides a framework for building drag and drop
19283  * applications.  In addition to enabling drag and drop for specific elements,
19284  * the drag and drop elements are tracked by the manager class, and the
19285  * interactions between the various elements are tracked during the drag and
19286  * the implementing code is notified about these important moments.
19287  */
19288
19289 // Only load the library once.  Rewriting the manager class would orphan
19290 // existing drag and drop instances.
19291 if (!Roo.dd.DragDropMgr) {
19292
19293 /**
19294  * @class Roo.dd.DragDropMgr
19295  * DragDropMgr is a singleton that tracks the element interaction for
19296  * all DragDrop items in the window.  Generally, you will not call
19297  * this class directly, but it does have helper methods that could
19298  * be useful in your DragDrop implementations.
19299  * @singleton
19300  */
19301 Roo.dd.DragDropMgr = function() {
19302
19303     var Event = Roo.EventManager;
19304
19305     return {
19306
19307         /**
19308          * Two dimensional Array of registered DragDrop objects.  The first
19309          * dimension is the DragDrop item group, the second the DragDrop
19310          * object.
19311          * @property ids
19312          * @type {string: string}
19313          * @private
19314          * @static
19315          */
19316         ids: {},
19317
19318         /**
19319          * Array of element ids defined as drag handles.  Used to determine
19320          * if the element that generated the mousedown event is actually the
19321          * handle and not the html element itself.
19322          * @property handleIds
19323          * @type {string: string}
19324          * @private
19325          * @static
19326          */
19327         handleIds: {},
19328
19329         /**
19330          * the DragDrop object that is currently being dragged
19331          * @property dragCurrent
19332          * @type DragDrop
19333          * @private
19334          * @static
19335          **/
19336         dragCurrent: null,
19337
19338         /**
19339          * the DragDrop object(s) that are being hovered over
19340          * @property dragOvers
19341          * @type Array
19342          * @private
19343          * @static
19344          */
19345         dragOvers: {},
19346
19347         /**
19348          * the X distance between the cursor and the object being dragged
19349          * @property deltaX
19350          * @type int
19351          * @private
19352          * @static
19353          */
19354         deltaX: 0,
19355
19356         /**
19357          * the Y distance between the cursor and the object being dragged
19358          * @property deltaY
19359          * @type int
19360          * @private
19361          * @static
19362          */
19363         deltaY: 0,
19364
19365         /**
19366          * Flag to determine if we should prevent the default behavior of the
19367          * events we define. By default this is true, but this can be set to
19368          * false if you need the default behavior (not recommended)
19369          * @property preventDefault
19370          * @type boolean
19371          * @static
19372          */
19373         preventDefault: true,
19374
19375         /**
19376          * Flag to determine if we should stop the propagation of the events
19377          * we generate. This is true by default but you may want to set it to
19378          * false if the html element contains other features that require the
19379          * mouse click.
19380          * @property stopPropagation
19381          * @type boolean
19382          * @static
19383          */
19384         stopPropagation: true,
19385
19386         /**
19387          * Internal flag that is set to true when drag and drop has been
19388          * intialized
19389          * @property initialized
19390          * @private
19391          * @static
19392          */
19393         initalized: false,
19394
19395         /**
19396          * All drag and drop can be disabled.
19397          * @property locked
19398          * @private
19399          * @static
19400          */
19401         locked: false,
19402
19403         /**
19404          * Called the first time an element is registered.
19405          * @method init
19406          * @private
19407          * @static
19408          */
19409         init: function() {
19410             this.initialized = true;
19411         },
19412
19413         /**
19414          * In point mode, drag and drop interaction is defined by the
19415          * location of the cursor during the drag/drop
19416          * @property POINT
19417          * @type int
19418          * @static
19419          */
19420         POINT: 0,
19421
19422         /**
19423          * In intersect mode, drag and drop interactio nis defined by the
19424          * overlap of two or more drag and drop objects.
19425          * @property INTERSECT
19426          * @type int
19427          * @static
19428          */
19429         INTERSECT: 1,
19430
19431         /**
19432          * The current drag and drop mode.  Default: POINT
19433          * @property mode
19434          * @type int
19435          * @static
19436          */
19437         mode: 0,
19438
19439         /**
19440          * Runs method on all drag and drop objects
19441          * @method _execOnAll
19442          * @private
19443          * @static
19444          */
19445         _execOnAll: function(sMethod, args) {
19446             for (var i in this.ids) {
19447                 for (var j in this.ids[i]) {
19448                     var oDD = this.ids[i][j];
19449                     if (! this.isTypeOfDD(oDD)) {
19450                         continue;
19451                     }
19452                     oDD[sMethod].apply(oDD, args);
19453                 }
19454             }
19455         },
19456
19457         /**
19458          * Drag and drop initialization.  Sets up the global event handlers
19459          * @method _onLoad
19460          * @private
19461          * @static
19462          */
19463         _onLoad: function() {
19464
19465             this.init();
19466
19467             if (!Roo.isTouch) {
19468                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19469                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19470             }
19471             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19472             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19473             
19474             Event.on(window,   "unload",    this._onUnload, this, true);
19475             Event.on(window,   "resize",    this._onResize, this, true);
19476             // Event.on(window,   "mouseout",    this._test);
19477
19478         },
19479
19480         /**
19481          * Reset constraints on all drag and drop objs
19482          * @method _onResize
19483          * @private
19484          * @static
19485          */
19486         _onResize: function(e) {
19487             this._execOnAll("resetConstraints", []);
19488         },
19489
19490         /**
19491          * Lock all drag and drop functionality
19492          * @method lock
19493          * @static
19494          */
19495         lock: function() { this.locked = true; },
19496
19497         /**
19498          * Unlock all drag and drop functionality
19499          * @method unlock
19500          * @static
19501          */
19502         unlock: function() { this.locked = false; },
19503
19504         /**
19505          * Is drag and drop locked?
19506          * @method isLocked
19507          * @return {boolean} True if drag and drop is locked, false otherwise.
19508          * @static
19509          */
19510         isLocked: function() { return this.locked; },
19511
19512         /**
19513          * Location cache that is set for all drag drop objects when a drag is
19514          * initiated, cleared when the drag is finished.
19515          * @property locationCache
19516          * @private
19517          * @static
19518          */
19519         locationCache: {},
19520
19521         /**
19522          * Set useCache to false if you want to force object the lookup of each
19523          * drag and drop linked element constantly during a drag.
19524          * @property useCache
19525          * @type boolean
19526          * @static
19527          */
19528         useCache: true,
19529
19530         /**
19531          * The number of pixels that the mouse needs to move after the
19532          * mousedown before the drag is initiated.  Default=3;
19533          * @property clickPixelThresh
19534          * @type int
19535          * @static
19536          */
19537         clickPixelThresh: 3,
19538
19539         /**
19540          * The number of milliseconds after the mousedown event to initiate the
19541          * drag if we don't get a mouseup event. Default=1000
19542          * @property clickTimeThresh
19543          * @type int
19544          * @static
19545          */
19546         clickTimeThresh: 350,
19547
19548         /**
19549          * Flag that indicates that either the drag pixel threshold or the
19550          * mousdown time threshold has been met
19551          * @property dragThreshMet
19552          * @type boolean
19553          * @private
19554          * @static
19555          */
19556         dragThreshMet: false,
19557
19558         /**
19559          * Timeout used for the click time threshold
19560          * @property clickTimeout
19561          * @type Object
19562          * @private
19563          * @static
19564          */
19565         clickTimeout: null,
19566
19567         /**
19568          * The X position of the mousedown event stored for later use when a
19569          * drag threshold is met.
19570          * @property startX
19571          * @type int
19572          * @private
19573          * @static
19574          */
19575         startX: 0,
19576
19577         /**
19578          * The Y position of the mousedown event stored for later use when a
19579          * drag threshold is met.
19580          * @property startY
19581          * @type int
19582          * @private
19583          * @static
19584          */
19585         startY: 0,
19586
19587         /**
19588          * Each DragDrop instance must be registered with the DragDropMgr.
19589          * This is executed in DragDrop.init()
19590          * @method regDragDrop
19591          * @param {DragDrop} oDD the DragDrop object to register
19592          * @param {String} sGroup the name of the group this element belongs to
19593          * @static
19594          */
19595         regDragDrop: function(oDD, sGroup) {
19596             if (!this.initialized) { this.init(); }
19597
19598             if (!this.ids[sGroup]) {
19599                 this.ids[sGroup] = {};
19600             }
19601             this.ids[sGroup][oDD.id] = oDD;
19602         },
19603
19604         /**
19605          * Removes the supplied dd instance from the supplied group. Executed
19606          * by DragDrop.removeFromGroup, so don't call this function directly.
19607          * @method removeDDFromGroup
19608          * @private
19609          * @static
19610          */
19611         removeDDFromGroup: function(oDD, sGroup) {
19612             if (!this.ids[sGroup]) {
19613                 this.ids[sGroup] = {};
19614             }
19615
19616             var obj = this.ids[sGroup];
19617             if (obj && obj[oDD.id]) {
19618                 delete obj[oDD.id];
19619             }
19620         },
19621
19622         /**
19623          * Unregisters a drag and drop item.  This is executed in
19624          * DragDrop.unreg, use that method instead of calling this directly.
19625          * @method _remove
19626          * @private
19627          * @static
19628          */
19629         _remove: function(oDD) {
19630             for (var g in oDD.groups) {
19631                 if (g && this.ids[g][oDD.id]) {
19632                     delete this.ids[g][oDD.id];
19633                 }
19634             }
19635             delete this.handleIds[oDD.id];
19636         },
19637
19638         /**
19639          * Each DragDrop handle element must be registered.  This is done
19640          * automatically when executing DragDrop.setHandleElId()
19641          * @method regHandle
19642          * @param {String} sDDId the DragDrop id this element is a handle for
19643          * @param {String} sHandleId the id of the element that is the drag
19644          * handle
19645          * @static
19646          */
19647         regHandle: function(sDDId, sHandleId) {
19648             if (!this.handleIds[sDDId]) {
19649                 this.handleIds[sDDId] = {};
19650             }
19651             this.handleIds[sDDId][sHandleId] = sHandleId;
19652         },
19653
19654         /**
19655          * Utility function to determine if a given element has been
19656          * registered as a drag drop item.
19657          * @method isDragDrop
19658          * @param {String} id the element id to check
19659          * @return {boolean} true if this element is a DragDrop item,
19660          * false otherwise
19661          * @static
19662          */
19663         isDragDrop: function(id) {
19664             return ( this.getDDById(id) ) ? true : false;
19665         },
19666
19667         /**
19668          * Returns the drag and drop instances that are in all groups the
19669          * passed in instance belongs to.
19670          * @method getRelated
19671          * @param {DragDrop} p_oDD the obj to get related data for
19672          * @param {boolean} bTargetsOnly if true, only return targetable objs
19673          * @return {DragDrop[]} the related instances
19674          * @static
19675          */
19676         getRelated: function(p_oDD, bTargetsOnly) {
19677             var oDDs = [];
19678             for (var i in p_oDD.groups) {
19679                 for (j in this.ids[i]) {
19680                     var dd = this.ids[i][j];
19681                     if (! this.isTypeOfDD(dd)) {
19682                         continue;
19683                     }
19684                     if (!bTargetsOnly || dd.isTarget) {
19685                         oDDs[oDDs.length] = dd;
19686                     }
19687                 }
19688             }
19689
19690             return oDDs;
19691         },
19692
19693         /**
19694          * Returns true if the specified dd target is a legal target for
19695          * the specifice drag obj
19696          * @method isLegalTarget
19697          * @param {DragDrop} the drag obj
19698          * @param {DragDrop} the target
19699          * @return {boolean} true if the target is a legal target for the
19700          * dd obj
19701          * @static
19702          */
19703         isLegalTarget: function (oDD, oTargetDD) {
19704             var targets = this.getRelated(oDD, true);
19705             for (var i=0, len=targets.length;i<len;++i) {
19706                 if (targets[i].id == oTargetDD.id) {
19707                     return true;
19708                 }
19709             }
19710
19711             return false;
19712         },
19713
19714         /**
19715          * My goal is to be able to transparently determine if an object is
19716          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19717          * returns "object", oDD.constructor.toString() always returns
19718          * "DragDrop" and not the name of the subclass.  So for now it just
19719          * evaluates a well-known variable in DragDrop.
19720          * @method isTypeOfDD
19721          * @param {Object} the object to evaluate
19722          * @return {boolean} true if typeof oDD = DragDrop
19723          * @static
19724          */
19725         isTypeOfDD: function (oDD) {
19726             return (oDD && oDD.__ygDragDrop);
19727         },
19728
19729         /**
19730          * Utility function to determine if a given element has been
19731          * registered as a drag drop handle for the given Drag Drop object.
19732          * @method isHandle
19733          * @param {String} id the element id to check
19734          * @return {boolean} true if this element is a DragDrop handle, false
19735          * otherwise
19736          * @static
19737          */
19738         isHandle: function(sDDId, sHandleId) {
19739             return ( this.handleIds[sDDId] &&
19740                             this.handleIds[sDDId][sHandleId] );
19741         },
19742
19743         /**
19744          * Returns the DragDrop instance for a given id
19745          * @method getDDById
19746          * @param {String} id the id of the DragDrop object
19747          * @return {DragDrop} the drag drop object, null if it is not found
19748          * @static
19749          */
19750         getDDById: function(id) {
19751             for (var i in this.ids) {
19752                 if (this.ids[i][id]) {
19753                     return this.ids[i][id];
19754                 }
19755             }
19756             return null;
19757         },
19758
19759         /**
19760          * Fired after a registered DragDrop object gets the mousedown event.
19761          * Sets up the events required to track the object being dragged
19762          * @method handleMouseDown
19763          * @param {Event} e the event
19764          * @param oDD the DragDrop object being dragged
19765          * @private
19766          * @static
19767          */
19768         handleMouseDown: function(e, oDD) {
19769             if(Roo.QuickTips){
19770                 Roo.QuickTips.disable();
19771             }
19772             this.currentTarget = e.getTarget();
19773
19774             this.dragCurrent = oDD;
19775
19776             var el = oDD.getEl();
19777
19778             // track start position
19779             this.startX = e.getPageX();
19780             this.startY = e.getPageY();
19781
19782             this.deltaX = this.startX - el.offsetLeft;
19783             this.deltaY = this.startY - el.offsetTop;
19784
19785             this.dragThreshMet = false;
19786
19787             this.clickTimeout = setTimeout(
19788                     function() {
19789                         var DDM = Roo.dd.DDM;
19790                         DDM.startDrag(DDM.startX, DDM.startY);
19791                     },
19792                     this.clickTimeThresh );
19793         },
19794
19795         /**
19796          * Fired when either the drag pixel threshol or the mousedown hold
19797          * time threshold has been met.
19798          * @method startDrag
19799          * @param x {int} the X position of the original mousedown
19800          * @param y {int} the Y position of the original mousedown
19801          * @static
19802          */
19803         startDrag: function(x, y) {
19804             clearTimeout(this.clickTimeout);
19805             if (this.dragCurrent) {
19806                 this.dragCurrent.b4StartDrag(x, y);
19807                 this.dragCurrent.startDrag(x, y);
19808             }
19809             this.dragThreshMet = true;
19810         },
19811
19812         /**
19813          * Internal function to handle the mouseup event.  Will be invoked
19814          * from the context of the document.
19815          * @method handleMouseUp
19816          * @param {Event} e the event
19817          * @private
19818          * @static
19819          */
19820         handleMouseUp: function(e) {
19821
19822             if(Roo.QuickTips){
19823                 Roo.QuickTips.enable();
19824             }
19825             if (! this.dragCurrent) {
19826                 return;
19827             }
19828
19829             clearTimeout(this.clickTimeout);
19830
19831             if (this.dragThreshMet) {
19832                 this.fireEvents(e, true);
19833             } else {
19834             }
19835
19836             this.stopDrag(e);
19837
19838             this.stopEvent(e);
19839         },
19840
19841         /**
19842          * Utility to stop event propagation and event default, if these
19843          * features are turned on.
19844          * @method stopEvent
19845          * @param {Event} e the event as returned by this.getEvent()
19846          * @static
19847          */
19848         stopEvent: function(e){
19849             if(this.stopPropagation) {
19850                 e.stopPropagation();
19851             }
19852
19853             if (this.preventDefault) {
19854                 e.preventDefault();
19855             }
19856         },
19857
19858         /**
19859          * Internal function to clean up event handlers after the drag
19860          * operation is complete
19861          * @method stopDrag
19862          * @param {Event} e the event
19863          * @private
19864          * @static
19865          */
19866         stopDrag: function(e) {
19867             // Fire the drag end event for the item that was dragged
19868             if (this.dragCurrent) {
19869                 if (this.dragThreshMet) {
19870                     this.dragCurrent.b4EndDrag(e);
19871                     this.dragCurrent.endDrag(e);
19872                 }
19873
19874                 this.dragCurrent.onMouseUp(e);
19875             }
19876
19877             this.dragCurrent = null;
19878             this.dragOvers = {};
19879         },
19880
19881         /**
19882          * Internal function to handle the mousemove event.  Will be invoked
19883          * from the context of the html element.
19884          *
19885          * @TODO figure out what we can do about mouse events lost when the
19886          * user drags objects beyond the window boundary.  Currently we can
19887          * detect this in internet explorer by verifying that the mouse is
19888          * down during the mousemove event.  Firefox doesn't give us the
19889          * button state on the mousemove event.
19890          * @method handleMouseMove
19891          * @param {Event} e the event
19892          * @private
19893          * @static
19894          */
19895         handleMouseMove: function(e) {
19896             if (! this.dragCurrent) {
19897                 return true;
19898             }
19899
19900             // var button = e.which || e.button;
19901
19902             // check for IE mouseup outside of page boundary
19903             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19904                 this.stopEvent(e);
19905                 return this.handleMouseUp(e);
19906             }
19907
19908             if (!this.dragThreshMet) {
19909                 var diffX = Math.abs(this.startX - e.getPageX());
19910                 var diffY = Math.abs(this.startY - e.getPageY());
19911                 if (diffX > this.clickPixelThresh ||
19912                             diffY > this.clickPixelThresh) {
19913                     this.startDrag(this.startX, this.startY);
19914                 }
19915             }
19916
19917             if (this.dragThreshMet) {
19918                 this.dragCurrent.b4Drag(e);
19919                 this.dragCurrent.onDrag(e);
19920                 if(!this.dragCurrent.moveOnly){
19921                     this.fireEvents(e, false);
19922                 }
19923             }
19924
19925             this.stopEvent(e);
19926
19927             return true;
19928         },
19929
19930         /**
19931          * Iterates over all of the DragDrop elements to find ones we are
19932          * hovering over or dropping on
19933          * @method fireEvents
19934          * @param {Event} e the event
19935          * @param {boolean} isDrop is this a drop op or a mouseover op?
19936          * @private
19937          * @static
19938          */
19939         fireEvents: function(e, isDrop) {
19940             var dc = this.dragCurrent;
19941
19942             // If the user did the mouse up outside of the window, we could
19943             // get here even though we have ended the drag.
19944             if (!dc || dc.isLocked()) {
19945                 return;
19946             }
19947
19948             var pt = e.getPoint();
19949
19950             // cache the previous dragOver array
19951             var oldOvers = [];
19952
19953             var outEvts   = [];
19954             var overEvts  = [];
19955             var dropEvts  = [];
19956             var enterEvts = [];
19957
19958             // Check to see if the object(s) we were hovering over is no longer
19959             // being hovered over so we can fire the onDragOut event
19960             for (var i in this.dragOvers) {
19961
19962                 var ddo = this.dragOvers[i];
19963
19964                 if (! this.isTypeOfDD(ddo)) {
19965                     continue;
19966                 }
19967
19968                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19969                     outEvts.push( ddo );
19970                 }
19971
19972                 oldOvers[i] = true;
19973                 delete this.dragOvers[i];
19974             }
19975
19976             for (var sGroup in dc.groups) {
19977
19978                 if ("string" != typeof sGroup) {
19979                     continue;
19980                 }
19981
19982                 for (i in this.ids[sGroup]) {
19983                     var oDD = this.ids[sGroup][i];
19984                     if (! this.isTypeOfDD(oDD)) {
19985                         continue;
19986                     }
19987
19988                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19989                         if (this.isOverTarget(pt, oDD, this.mode)) {
19990                             // look for drop interactions
19991                             if (isDrop) {
19992                                 dropEvts.push( oDD );
19993                             // look for drag enter and drag over interactions
19994                             } else {
19995
19996                                 // initial drag over: dragEnter fires
19997                                 if (!oldOvers[oDD.id]) {
19998                                     enterEvts.push( oDD );
19999                                 // subsequent drag overs: dragOver fires
20000                                 } else {
20001                                     overEvts.push( oDD );
20002                                 }
20003
20004                                 this.dragOvers[oDD.id] = oDD;
20005                             }
20006                         }
20007                     }
20008                 }
20009             }
20010
20011             if (this.mode) {
20012                 if (outEvts.length) {
20013                     dc.b4DragOut(e, outEvts);
20014                     dc.onDragOut(e, outEvts);
20015                 }
20016
20017                 if (enterEvts.length) {
20018                     dc.onDragEnter(e, enterEvts);
20019                 }
20020
20021                 if (overEvts.length) {
20022                     dc.b4DragOver(e, overEvts);
20023                     dc.onDragOver(e, overEvts);
20024                 }
20025
20026                 if (dropEvts.length) {
20027                     dc.b4DragDrop(e, dropEvts);
20028                     dc.onDragDrop(e, dropEvts);
20029                 }
20030
20031             } else {
20032                 // fire dragout events
20033                 var len = 0;
20034                 for (i=0, len=outEvts.length; i<len; ++i) {
20035                     dc.b4DragOut(e, outEvts[i].id);
20036                     dc.onDragOut(e, outEvts[i].id);
20037                 }
20038
20039                 // fire enter events
20040                 for (i=0,len=enterEvts.length; i<len; ++i) {
20041                     // dc.b4DragEnter(e, oDD.id);
20042                     dc.onDragEnter(e, enterEvts[i].id);
20043                 }
20044
20045                 // fire over events
20046                 for (i=0,len=overEvts.length; i<len; ++i) {
20047                     dc.b4DragOver(e, overEvts[i].id);
20048                     dc.onDragOver(e, overEvts[i].id);
20049                 }
20050
20051                 // fire drop events
20052                 for (i=0, len=dropEvts.length; i<len; ++i) {
20053                     dc.b4DragDrop(e, dropEvts[i].id);
20054                     dc.onDragDrop(e, dropEvts[i].id);
20055                 }
20056
20057             }
20058
20059             // notify about a drop that did not find a target
20060             if (isDrop && !dropEvts.length) {
20061                 dc.onInvalidDrop(e);
20062             }
20063
20064         },
20065
20066         /**
20067          * Helper function for getting the best match from the list of drag
20068          * and drop objects returned by the drag and drop events when we are
20069          * in INTERSECT mode.  It returns either the first object that the
20070          * cursor is over, or the object that has the greatest overlap with
20071          * the dragged element.
20072          * @method getBestMatch
20073          * @param  {DragDrop[]} dds The array of drag and drop objects
20074          * targeted
20075          * @return {DragDrop}       The best single match
20076          * @static
20077          */
20078         getBestMatch: function(dds) {
20079             var winner = null;
20080             // Return null if the input is not what we expect
20081             //if (!dds || !dds.length || dds.length == 0) {
20082                // winner = null;
20083             // If there is only one item, it wins
20084             //} else if (dds.length == 1) {
20085
20086             var len = dds.length;
20087
20088             if (len == 1) {
20089                 winner = dds[0];
20090             } else {
20091                 // Loop through the targeted items
20092                 for (var i=0; i<len; ++i) {
20093                     var dd = dds[i];
20094                     // If the cursor is over the object, it wins.  If the
20095                     // cursor is over multiple matches, the first one we come
20096                     // to wins.
20097                     if (dd.cursorIsOver) {
20098                         winner = dd;
20099                         break;
20100                     // Otherwise the object with the most overlap wins
20101                     } else {
20102                         if (!winner ||
20103                             winner.overlap.getArea() < dd.overlap.getArea()) {
20104                             winner = dd;
20105                         }
20106                     }
20107                 }
20108             }
20109
20110             return winner;
20111         },
20112
20113         /**
20114          * Refreshes the cache of the top-left and bottom-right points of the
20115          * drag and drop objects in the specified group(s).  This is in the
20116          * format that is stored in the drag and drop instance, so typical
20117          * usage is:
20118          * <code>
20119          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20120          * </code>
20121          * Alternatively:
20122          * <code>
20123          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20124          * </code>
20125          * @TODO this really should be an indexed array.  Alternatively this
20126          * method could accept both.
20127          * @method refreshCache
20128          * @param {Object} groups an associative array of groups to refresh
20129          * @static
20130          */
20131         refreshCache: function(groups) {
20132             for (var sGroup in groups) {
20133                 if ("string" != typeof sGroup) {
20134                     continue;
20135                 }
20136                 for (var i in this.ids[sGroup]) {
20137                     var oDD = this.ids[sGroup][i];
20138
20139                     if (this.isTypeOfDD(oDD)) {
20140                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20141                         var loc = this.getLocation(oDD);
20142                         if (loc) {
20143                             this.locationCache[oDD.id] = loc;
20144                         } else {
20145                             delete this.locationCache[oDD.id];
20146                             // this will unregister the drag and drop object if
20147                             // the element is not in a usable state
20148                             // oDD.unreg();
20149                         }
20150                     }
20151                 }
20152             }
20153         },
20154
20155         /**
20156          * This checks to make sure an element exists and is in the DOM.  The
20157          * main purpose is to handle cases where innerHTML is used to remove
20158          * drag and drop objects from the DOM.  IE provides an 'unspecified
20159          * error' when trying to access the offsetParent of such an element
20160          * @method verifyEl
20161          * @param {HTMLElement} el the element to check
20162          * @return {boolean} true if the element looks usable
20163          * @static
20164          */
20165         verifyEl: function(el) {
20166             if (el) {
20167                 var parent;
20168                 if(Roo.isIE){
20169                     try{
20170                         parent = el.offsetParent;
20171                     }catch(e){}
20172                 }else{
20173                     parent = el.offsetParent;
20174                 }
20175                 if (parent) {
20176                     return true;
20177                 }
20178             }
20179
20180             return false;
20181         },
20182
20183         /**
20184          * Returns a Region object containing the drag and drop element's position
20185          * and size, including the padding configured for it
20186          * @method getLocation
20187          * @param {DragDrop} oDD the drag and drop object to get the
20188          *                       location for
20189          * @return {Roo.lib.Region} a Region object representing the total area
20190          *                             the element occupies, including any padding
20191          *                             the instance is configured for.
20192          * @static
20193          */
20194         getLocation: function(oDD) {
20195             if (! this.isTypeOfDD(oDD)) {
20196                 return null;
20197             }
20198
20199             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20200
20201             try {
20202                 pos= Roo.lib.Dom.getXY(el);
20203             } catch (e) { }
20204
20205             if (!pos) {
20206                 return null;
20207             }
20208
20209             x1 = pos[0];
20210             x2 = x1 + el.offsetWidth;
20211             y1 = pos[1];
20212             y2 = y1 + el.offsetHeight;
20213
20214             t = y1 - oDD.padding[0];
20215             r = x2 + oDD.padding[1];
20216             b = y2 + oDD.padding[2];
20217             l = x1 - oDD.padding[3];
20218
20219             return new Roo.lib.Region( t, r, b, l );
20220         },
20221
20222         /**
20223          * Checks the cursor location to see if it over the target
20224          * @method isOverTarget
20225          * @param {Roo.lib.Point} pt The point to evaluate
20226          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20227          * @return {boolean} true if the mouse is over the target
20228          * @private
20229          * @static
20230          */
20231         isOverTarget: function(pt, oTarget, intersect) {
20232             // use cache if available
20233             var loc = this.locationCache[oTarget.id];
20234             if (!loc || !this.useCache) {
20235                 loc = this.getLocation(oTarget);
20236                 this.locationCache[oTarget.id] = loc;
20237
20238             }
20239
20240             if (!loc) {
20241                 return false;
20242             }
20243
20244             oTarget.cursorIsOver = loc.contains( pt );
20245
20246             // DragDrop is using this as a sanity check for the initial mousedown
20247             // in this case we are done.  In POINT mode, if the drag obj has no
20248             // contraints, we are also done. Otherwise we need to evaluate the
20249             // location of the target as related to the actual location of the
20250             // dragged element.
20251             var dc = this.dragCurrent;
20252             if (!dc || !dc.getTargetCoord ||
20253                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20254                 return oTarget.cursorIsOver;
20255             }
20256
20257             oTarget.overlap = null;
20258
20259             // Get the current location of the drag element, this is the
20260             // location of the mouse event less the delta that represents
20261             // where the original mousedown happened on the element.  We
20262             // need to consider constraints and ticks as well.
20263             var pos = dc.getTargetCoord(pt.x, pt.y);
20264
20265             var el = dc.getDragEl();
20266             var curRegion = new Roo.lib.Region( pos.y,
20267                                                    pos.x + el.offsetWidth,
20268                                                    pos.y + el.offsetHeight,
20269                                                    pos.x );
20270
20271             var overlap = curRegion.intersect(loc);
20272
20273             if (overlap) {
20274                 oTarget.overlap = overlap;
20275                 return (intersect) ? true : oTarget.cursorIsOver;
20276             } else {
20277                 return false;
20278             }
20279         },
20280
20281         /**
20282          * unload event handler
20283          * @method _onUnload
20284          * @private
20285          * @static
20286          */
20287         _onUnload: function(e, me) {
20288             Roo.dd.DragDropMgr.unregAll();
20289         },
20290
20291         /**
20292          * Cleans up the drag and drop events and objects.
20293          * @method unregAll
20294          * @private
20295          * @static
20296          */
20297         unregAll: function() {
20298
20299             if (this.dragCurrent) {
20300                 this.stopDrag();
20301                 this.dragCurrent = null;
20302             }
20303
20304             this._execOnAll("unreg", []);
20305
20306             for (i in this.elementCache) {
20307                 delete this.elementCache[i];
20308             }
20309
20310             this.elementCache = {};
20311             this.ids = {};
20312         },
20313
20314         /**
20315          * A cache of DOM elements
20316          * @property elementCache
20317          * @private
20318          * @static
20319          */
20320         elementCache: {},
20321
20322         /**
20323          * Get the wrapper for the DOM element specified
20324          * @method getElWrapper
20325          * @param {String} id the id of the element to get
20326          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20327          * @private
20328          * @deprecated This wrapper isn't that useful
20329          * @static
20330          */
20331         getElWrapper: function(id) {
20332             var oWrapper = this.elementCache[id];
20333             if (!oWrapper || !oWrapper.el) {
20334                 oWrapper = this.elementCache[id] =
20335                     new this.ElementWrapper(Roo.getDom(id));
20336             }
20337             return oWrapper;
20338         },
20339
20340         /**
20341          * Returns the actual DOM element
20342          * @method getElement
20343          * @param {String} id the id of the elment to get
20344          * @return {Object} The element
20345          * @deprecated use Roo.getDom instead
20346          * @static
20347          */
20348         getElement: function(id) {
20349             return Roo.getDom(id);
20350         },
20351
20352         /**
20353          * Returns the style property for the DOM element (i.e.,
20354          * document.getElById(id).style)
20355          * @method getCss
20356          * @param {String} id the id of the elment to get
20357          * @return {Object} The style property of the element
20358          * @deprecated use Roo.getDom instead
20359          * @static
20360          */
20361         getCss: function(id) {
20362             var el = Roo.getDom(id);
20363             return (el) ? el.style : null;
20364         },
20365
20366         /**
20367          * Inner class for cached elements
20368          * @class DragDropMgr.ElementWrapper
20369          * @for DragDropMgr
20370          * @private
20371          * @deprecated
20372          */
20373         ElementWrapper: function(el) {
20374                 /**
20375                  * The element
20376                  * @property el
20377                  */
20378                 this.el = el || null;
20379                 /**
20380                  * The element id
20381                  * @property id
20382                  */
20383                 this.id = this.el && el.id;
20384                 /**
20385                  * A reference to the style property
20386                  * @property css
20387                  */
20388                 this.css = this.el && el.style;
20389             },
20390
20391         /**
20392          * Returns the X position of an html element
20393          * @method getPosX
20394          * @param el the element for which to get the position
20395          * @return {int} the X coordinate
20396          * @for DragDropMgr
20397          * @deprecated use Roo.lib.Dom.getX instead
20398          * @static
20399          */
20400         getPosX: function(el) {
20401             return Roo.lib.Dom.getX(el);
20402         },
20403
20404         /**
20405          * Returns the Y position of an html element
20406          * @method getPosY
20407          * @param el the element for which to get the position
20408          * @return {int} the Y coordinate
20409          * @deprecated use Roo.lib.Dom.getY instead
20410          * @static
20411          */
20412         getPosY: function(el) {
20413             return Roo.lib.Dom.getY(el);
20414         },
20415
20416         /**
20417          * Swap two nodes.  In IE, we use the native method, for others we
20418          * emulate the IE behavior
20419          * @method swapNode
20420          * @param n1 the first node to swap
20421          * @param n2 the other node to swap
20422          * @static
20423          */
20424         swapNode: function(n1, n2) {
20425             if (n1.swapNode) {
20426                 n1.swapNode(n2);
20427             } else {
20428                 var p = n2.parentNode;
20429                 var s = n2.nextSibling;
20430
20431                 if (s == n1) {
20432                     p.insertBefore(n1, n2);
20433                 } else if (n2 == n1.nextSibling) {
20434                     p.insertBefore(n2, n1);
20435                 } else {
20436                     n1.parentNode.replaceChild(n2, n1);
20437                     p.insertBefore(n1, s);
20438                 }
20439             }
20440         },
20441
20442         /**
20443          * Returns the current scroll position
20444          * @method getScroll
20445          * @private
20446          * @static
20447          */
20448         getScroll: function () {
20449             var t, l, dde=document.documentElement, db=document.body;
20450             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20451                 t = dde.scrollTop;
20452                 l = dde.scrollLeft;
20453             } else if (db) {
20454                 t = db.scrollTop;
20455                 l = db.scrollLeft;
20456             } else {
20457
20458             }
20459             return { top: t, left: l };
20460         },
20461
20462         /**
20463          * Returns the specified element style property
20464          * @method getStyle
20465          * @param {HTMLElement} el          the element
20466          * @param {string}      styleProp   the style property
20467          * @return {string} The value of the style property
20468          * @deprecated use Roo.lib.Dom.getStyle
20469          * @static
20470          */
20471         getStyle: function(el, styleProp) {
20472             return Roo.fly(el).getStyle(styleProp);
20473         },
20474
20475         /**
20476          * Gets the scrollTop
20477          * @method getScrollTop
20478          * @return {int} the document's scrollTop
20479          * @static
20480          */
20481         getScrollTop: function () { return this.getScroll().top; },
20482
20483         /**
20484          * Gets the scrollLeft
20485          * @method getScrollLeft
20486          * @return {int} the document's scrollTop
20487          * @static
20488          */
20489         getScrollLeft: function () { return this.getScroll().left; },
20490
20491         /**
20492          * Sets the x/y position of an element to the location of the
20493          * target element.
20494          * @method moveToEl
20495          * @param {HTMLElement} moveEl      The element to move
20496          * @param {HTMLElement} targetEl    The position reference element
20497          * @static
20498          */
20499         moveToEl: function (moveEl, targetEl) {
20500             var aCoord = Roo.lib.Dom.getXY(targetEl);
20501             Roo.lib.Dom.setXY(moveEl, aCoord);
20502         },
20503
20504         /**
20505          * Numeric array sort function
20506          * @method numericSort
20507          * @static
20508          */
20509         numericSort: function(a, b) { return (a - b); },
20510
20511         /**
20512          * Internal counter
20513          * @property _timeoutCount
20514          * @private
20515          * @static
20516          */
20517         _timeoutCount: 0,
20518
20519         /**
20520          * Trying to make the load order less important.  Without this we get
20521          * an error if this file is loaded before the Event Utility.
20522          * @method _addListeners
20523          * @private
20524          * @static
20525          */
20526         _addListeners: function() {
20527             var DDM = Roo.dd.DDM;
20528             if ( Roo.lib.Event && document ) {
20529                 DDM._onLoad();
20530             } else {
20531                 if (DDM._timeoutCount > 2000) {
20532                 } else {
20533                     setTimeout(DDM._addListeners, 10);
20534                     if (document && document.body) {
20535                         DDM._timeoutCount += 1;
20536                     }
20537                 }
20538             }
20539         },
20540
20541         /**
20542          * Recursively searches the immediate parent and all child nodes for
20543          * the handle element in order to determine wheter or not it was
20544          * clicked.
20545          * @method handleWasClicked
20546          * @param node the html element to inspect
20547          * @static
20548          */
20549         handleWasClicked: function(node, id) {
20550             if (this.isHandle(id, node.id)) {
20551                 return true;
20552             } else {
20553                 // check to see if this is a text node child of the one we want
20554                 var p = node.parentNode;
20555
20556                 while (p) {
20557                     if (this.isHandle(id, p.id)) {
20558                         return true;
20559                     } else {
20560                         p = p.parentNode;
20561                     }
20562                 }
20563             }
20564
20565             return false;
20566         }
20567
20568     };
20569
20570 }();
20571
20572 // shorter alias, save a few bytes
20573 Roo.dd.DDM = Roo.dd.DragDropMgr;
20574 Roo.dd.DDM._addListeners();
20575
20576 }/*
20577  * Based on:
20578  * Ext JS Library 1.1.1
20579  * Copyright(c) 2006-2007, Ext JS, LLC.
20580  *
20581  * Originally Released Under LGPL - original licence link has changed is not relivant.
20582  *
20583  * Fork - LGPL
20584  * <script type="text/javascript">
20585  */
20586
20587 /**
20588  * @class Roo.dd.DD
20589  * A DragDrop implementation where the linked element follows the
20590  * mouse cursor during a drag.
20591  * @extends Roo.dd.DragDrop
20592  * @constructor
20593  * @param {String} id the id of the linked element
20594  * @param {String} sGroup the group of related DragDrop items
20595  * @param {object} config an object containing configurable attributes
20596  *                Valid properties for DD:
20597  *                    scroll
20598  */
20599 Roo.dd.DD = function(id, sGroup, config) {
20600     if (id) {
20601         this.init(id, sGroup, config);
20602     }
20603 };
20604
20605 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20606
20607     /**
20608      * When set to true, the utility automatically tries to scroll the browser
20609      * window wehn a drag and drop element is dragged near the viewport boundary.
20610      * Defaults to true.
20611      * @property scroll
20612      * @type boolean
20613      */
20614     scroll: true,
20615
20616     /**
20617      * Sets the pointer offset to the distance between the linked element's top
20618      * left corner and the location the element was clicked
20619      * @method autoOffset
20620      * @param {int} iPageX the X coordinate of the click
20621      * @param {int} iPageY the Y coordinate of the click
20622      */
20623     autoOffset: function(iPageX, iPageY) {
20624         var x = iPageX - this.startPageX;
20625         var y = iPageY - this.startPageY;
20626         this.setDelta(x, y);
20627     },
20628
20629     /**
20630      * Sets the pointer offset.  You can call this directly to force the
20631      * offset to be in a particular location (e.g., pass in 0,0 to set it
20632      * to the center of the object)
20633      * @method setDelta
20634      * @param {int} iDeltaX the distance from the left
20635      * @param {int} iDeltaY the distance from the top
20636      */
20637     setDelta: function(iDeltaX, iDeltaY) {
20638         this.deltaX = iDeltaX;
20639         this.deltaY = iDeltaY;
20640     },
20641
20642     /**
20643      * Sets the drag element to the location of the mousedown or click event,
20644      * maintaining the cursor location relative to the location on the element
20645      * that was clicked.  Override this if you want to place the element in a
20646      * location other than where the cursor is.
20647      * @method setDragElPos
20648      * @param {int} iPageX the X coordinate of the mousedown or drag event
20649      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20650      */
20651     setDragElPos: function(iPageX, iPageY) {
20652         // the first time we do this, we are going to check to make sure
20653         // the element has css positioning
20654
20655         var el = this.getDragEl();
20656         this.alignElWithMouse(el, iPageX, iPageY);
20657     },
20658
20659     /**
20660      * Sets the element to the location of the mousedown or click event,
20661      * maintaining the cursor location relative to the location on the element
20662      * that was clicked.  Override this if you want to place the element in a
20663      * location other than where the cursor is.
20664      * @method alignElWithMouse
20665      * @param {HTMLElement} el the element to move
20666      * @param {int} iPageX the X coordinate of the mousedown or drag event
20667      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20668      */
20669     alignElWithMouse: function(el, iPageX, iPageY) {
20670         var oCoord = this.getTargetCoord(iPageX, iPageY);
20671         var fly = el.dom ? el : Roo.fly(el);
20672         if (!this.deltaSetXY) {
20673             var aCoord = [oCoord.x, oCoord.y];
20674             fly.setXY(aCoord);
20675             var newLeft = fly.getLeft(true);
20676             var newTop  = fly.getTop(true);
20677             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20678         } else {
20679             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20680         }
20681
20682         this.cachePosition(oCoord.x, oCoord.y);
20683         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20684         return oCoord;
20685     },
20686
20687     /**
20688      * Saves the most recent position so that we can reset the constraints and
20689      * tick marks on-demand.  We need to know this so that we can calculate the
20690      * number of pixels the element is offset from its original position.
20691      * @method cachePosition
20692      * @param iPageX the current x position (optional, this just makes it so we
20693      * don't have to look it up again)
20694      * @param iPageY the current y position (optional, this just makes it so we
20695      * don't have to look it up again)
20696      */
20697     cachePosition: function(iPageX, iPageY) {
20698         if (iPageX) {
20699             this.lastPageX = iPageX;
20700             this.lastPageY = iPageY;
20701         } else {
20702             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20703             this.lastPageX = aCoord[0];
20704             this.lastPageY = aCoord[1];
20705         }
20706     },
20707
20708     /**
20709      * Auto-scroll the window if the dragged object has been moved beyond the
20710      * visible window boundary.
20711      * @method autoScroll
20712      * @param {int} x the drag element's x position
20713      * @param {int} y the drag element's y position
20714      * @param {int} h the height of the drag element
20715      * @param {int} w the width of the drag element
20716      * @private
20717      */
20718     autoScroll: function(x, y, h, w) {
20719
20720         if (this.scroll) {
20721             // The client height
20722             var clientH = Roo.lib.Dom.getViewWidth();
20723
20724             // The client width
20725             var clientW = Roo.lib.Dom.getViewHeight();
20726
20727             // The amt scrolled down
20728             var st = this.DDM.getScrollTop();
20729
20730             // The amt scrolled right
20731             var sl = this.DDM.getScrollLeft();
20732
20733             // Location of the bottom of the element
20734             var bot = h + y;
20735
20736             // Location of the right of the element
20737             var right = w + x;
20738
20739             // The distance from the cursor to the bottom of the visible area,
20740             // adjusted so that we don't scroll if the cursor is beyond the
20741             // element drag constraints
20742             var toBot = (clientH + st - y - this.deltaY);
20743
20744             // The distance from the cursor to the right of the visible area
20745             var toRight = (clientW + sl - x - this.deltaX);
20746
20747
20748             // How close to the edge the cursor must be before we scroll
20749             // var thresh = (document.all) ? 100 : 40;
20750             var thresh = 40;
20751
20752             // How many pixels to scroll per autoscroll op.  This helps to reduce
20753             // clunky scrolling. IE is more sensitive about this ... it needs this
20754             // value to be higher.
20755             var scrAmt = (document.all) ? 80 : 30;
20756
20757             // Scroll down if we are near the bottom of the visible page and the
20758             // obj extends below the crease
20759             if ( bot > clientH && toBot < thresh ) {
20760                 window.scrollTo(sl, st + scrAmt);
20761             }
20762
20763             // Scroll up if the window is scrolled down and the top of the object
20764             // goes above the top border
20765             if ( y < st && st > 0 && y - st < thresh ) {
20766                 window.scrollTo(sl, st - scrAmt);
20767             }
20768
20769             // Scroll right if the obj is beyond the right border and the cursor is
20770             // near the border.
20771             if ( right > clientW && toRight < thresh ) {
20772                 window.scrollTo(sl + scrAmt, st);
20773             }
20774
20775             // Scroll left if the window has been scrolled to the right and the obj
20776             // extends past the left border
20777             if ( x < sl && sl > 0 && x - sl < thresh ) {
20778                 window.scrollTo(sl - scrAmt, st);
20779             }
20780         }
20781     },
20782
20783     /**
20784      * Finds the location the element should be placed if we want to move
20785      * it to where the mouse location less the click offset would place us.
20786      * @method getTargetCoord
20787      * @param {int} iPageX the X coordinate of the click
20788      * @param {int} iPageY the Y coordinate of the click
20789      * @return an object that contains the coordinates (Object.x and Object.y)
20790      * @private
20791      */
20792     getTargetCoord: function(iPageX, iPageY) {
20793
20794
20795         var x = iPageX - this.deltaX;
20796         var y = iPageY - this.deltaY;
20797
20798         if (this.constrainX) {
20799             if (x < this.minX) { x = this.minX; }
20800             if (x > this.maxX) { x = this.maxX; }
20801         }
20802
20803         if (this.constrainY) {
20804             if (y < this.minY) { y = this.minY; }
20805             if (y > this.maxY) { y = this.maxY; }
20806         }
20807
20808         x = this.getTick(x, this.xTicks);
20809         y = this.getTick(y, this.yTicks);
20810
20811
20812         return {x:x, y:y};
20813     },
20814
20815     /*
20816      * Sets up config options specific to this class. Overrides
20817      * Roo.dd.DragDrop, but all versions of this method through the
20818      * inheritance chain are called
20819      */
20820     applyConfig: function() {
20821         Roo.dd.DD.superclass.applyConfig.call(this);
20822         this.scroll = (this.config.scroll !== false);
20823     },
20824
20825     /*
20826      * Event that fires prior to the onMouseDown event.  Overrides
20827      * Roo.dd.DragDrop.
20828      */
20829     b4MouseDown: function(e) {
20830         // this.resetConstraints();
20831         this.autoOffset(e.getPageX(),
20832                             e.getPageY());
20833     },
20834
20835     /*
20836      * Event that fires prior to the onDrag event.  Overrides
20837      * Roo.dd.DragDrop.
20838      */
20839     b4Drag: function(e) {
20840         this.setDragElPos(e.getPageX(),
20841                             e.getPageY());
20842     },
20843
20844     toString: function() {
20845         return ("DD " + this.id);
20846     }
20847
20848     //////////////////////////////////////////////////////////////////////////
20849     // Debugging ygDragDrop events that can be overridden
20850     //////////////////////////////////////////////////////////////////////////
20851     /*
20852     startDrag: function(x, y) {
20853     },
20854
20855     onDrag: function(e) {
20856     },
20857
20858     onDragEnter: function(e, id) {
20859     },
20860
20861     onDragOver: function(e, id) {
20862     },
20863
20864     onDragOut: function(e, id) {
20865     },
20866
20867     onDragDrop: function(e, id) {
20868     },
20869
20870     endDrag: function(e) {
20871     }
20872
20873     */
20874
20875 });/*
20876  * Based on:
20877  * Ext JS Library 1.1.1
20878  * Copyright(c) 2006-2007, Ext JS, LLC.
20879  *
20880  * Originally Released Under LGPL - original licence link has changed is not relivant.
20881  *
20882  * Fork - LGPL
20883  * <script type="text/javascript">
20884  */
20885
20886 /**
20887  * @class Roo.dd.DDProxy
20888  * A DragDrop implementation that inserts an empty, bordered div into
20889  * the document that follows the cursor during drag operations.  At the time of
20890  * the click, the frame div is resized to the dimensions of the linked html
20891  * element, and moved to the exact location of the linked element.
20892  *
20893  * References to the "frame" element refer to the single proxy element that
20894  * was created to be dragged in place of all DDProxy elements on the
20895  * page.
20896  *
20897  * @extends Roo.dd.DD
20898  * @constructor
20899  * @param {String} id the id of the linked html element
20900  * @param {String} sGroup the group of related DragDrop objects
20901  * @param {object} config an object containing configurable attributes
20902  *                Valid properties for DDProxy in addition to those in DragDrop:
20903  *                   resizeFrame, centerFrame, dragElId
20904  */
20905 Roo.dd.DDProxy = function(id, sGroup, config) {
20906     if (id) {
20907         this.init(id, sGroup, config);
20908         this.initFrame();
20909     }
20910 };
20911
20912 /**
20913  * The default drag frame div id
20914  * @property Roo.dd.DDProxy.dragElId
20915  * @type String
20916  * @static
20917  */
20918 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20919
20920 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20921
20922     /**
20923      * By default we resize the drag frame to be the same size as the element
20924      * we want to drag (this is to get the frame effect).  We can turn it off
20925      * if we want a different behavior.
20926      * @property resizeFrame
20927      * @type boolean
20928      */
20929     resizeFrame: true,
20930
20931     /**
20932      * By default the frame is positioned exactly where the drag element is, so
20933      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20934      * you do not have constraints on the obj is to have the drag frame centered
20935      * around the cursor.  Set centerFrame to true for this effect.
20936      * @property centerFrame
20937      * @type boolean
20938      */
20939     centerFrame: false,
20940
20941     /**
20942      * Creates the proxy element if it does not yet exist
20943      * @method createFrame
20944      */
20945     createFrame: function() {
20946         var self = this;
20947         var body = document.body;
20948
20949         if (!body || !body.firstChild) {
20950             setTimeout( function() { self.createFrame(); }, 50 );
20951             return;
20952         }
20953
20954         var div = this.getDragEl();
20955
20956         if (!div) {
20957             div    = document.createElement("div");
20958             div.id = this.dragElId;
20959             var s  = div.style;
20960
20961             s.position   = "absolute";
20962             s.visibility = "hidden";
20963             s.cursor     = "move";
20964             s.border     = "2px solid #aaa";
20965             s.zIndex     = 999;
20966
20967             // appendChild can blow up IE if invoked prior to the window load event
20968             // while rendering a table.  It is possible there are other scenarios
20969             // that would cause this to happen as well.
20970             body.insertBefore(div, body.firstChild);
20971         }
20972     },
20973
20974     /**
20975      * Initialization for the drag frame element.  Must be called in the
20976      * constructor of all subclasses
20977      * @method initFrame
20978      */
20979     initFrame: function() {
20980         this.createFrame();
20981     },
20982
20983     applyConfig: function() {
20984         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20985
20986         this.resizeFrame = (this.config.resizeFrame !== false);
20987         this.centerFrame = (this.config.centerFrame);
20988         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20989     },
20990
20991     /**
20992      * Resizes the drag frame to the dimensions of the clicked object, positions
20993      * it over the object, and finally displays it
20994      * @method showFrame
20995      * @param {int} iPageX X click position
20996      * @param {int} iPageY Y click position
20997      * @private
20998      */
20999     showFrame: function(iPageX, iPageY) {
21000         var el = this.getEl();
21001         var dragEl = this.getDragEl();
21002         var s = dragEl.style;
21003
21004         this._resizeProxy();
21005
21006         if (this.centerFrame) {
21007             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21008                            Math.round(parseInt(s.height, 10)/2) );
21009         }
21010
21011         this.setDragElPos(iPageX, iPageY);
21012
21013         Roo.fly(dragEl).show();
21014     },
21015
21016     /**
21017      * The proxy is automatically resized to the dimensions of the linked
21018      * element when a drag is initiated, unless resizeFrame is set to false
21019      * @method _resizeProxy
21020      * @private
21021      */
21022     _resizeProxy: function() {
21023         if (this.resizeFrame) {
21024             var el = this.getEl();
21025             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21026         }
21027     },
21028
21029     // overrides Roo.dd.DragDrop
21030     b4MouseDown: function(e) {
21031         var x = e.getPageX();
21032         var y = e.getPageY();
21033         this.autoOffset(x, y);
21034         this.setDragElPos(x, y);
21035     },
21036
21037     // overrides Roo.dd.DragDrop
21038     b4StartDrag: function(x, y) {
21039         // show the drag frame
21040         this.showFrame(x, y);
21041     },
21042
21043     // overrides Roo.dd.DragDrop
21044     b4EndDrag: function(e) {
21045         Roo.fly(this.getDragEl()).hide();
21046     },
21047
21048     // overrides Roo.dd.DragDrop
21049     // By default we try to move the element to the last location of the frame.
21050     // This is so that the default behavior mirrors that of Roo.dd.DD.
21051     endDrag: function(e) {
21052
21053         var lel = this.getEl();
21054         var del = this.getDragEl();
21055
21056         // Show the drag frame briefly so we can get its position
21057         del.style.visibility = "";
21058
21059         this.beforeMove();
21060         // Hide the linked element before the move to get around a Safari
21061         // rendering bug.
21062         lel.style.visibility = "hidden";
21063         Roo.dd.DDM.moveToEl(lel, del);
21064         del.style.visibility = "hidden";
21065         lel.style.visibility = "";
21066
21067         this.afterDrag();
21068     },
21069
21070     beforeMove : function(){
21071
21072     },
21073
21074     afterDrag : function(){
21075
21076     },
21077
21078     toString: function() {
21079         return ("DDProxy " + this.id);
21080     }
21081
21082 });
21083 /*
21084  * Based on:
21085  * Ext JS Library 1.1.1
21086  * Copyright(c) 2006-2007, Ext JS, LLC.
21087  *
21088  * Originally Released Under LGPL - original licence link has changed is not relivant.
21089  *
21090  * Fork - LGPL
21091  * <script type="text/javascript">
21092  */
21093
21094  /**
21095  * @class Roo.dd.DDTarget
21096  * A DragDrop implementation that does not move, but can be a drop
21097  * target.  You would get the same result by simply omitting implementation
21098  * for the event callbacks, but this way we reduce the processing cost of the
21099  * event listener and the callbacks.
21100  * @extends Roo.dd.DragDrop
21101  * @constructor
21102  * @param {String} id the id of the element that is a drop target
21103  * @param {String} sGroup the group of related DragDrop objects
21104  * @param {object} config an object containing configurable attributes
21105  *                 Valid properties for DDTarget in addition to those in
21106  *                 DragDrop:
21107  *                    none
21108  */
21109 Roo.dd.DDTarget = function(id, sGroup, config) {
21110     if (id) {
21111         this.initTarget(id, sGroup, config);
21112     }
21113     if (config && (config.listeners || config.events)) { 
21114         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21115             listeners : config.listeners || {}, 
21116             events : config.events || {} 
21117         });    
21118     }
21119 };
21120
21121 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21122 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21123     toString: function() {
21124         return ("DDTarget " + this.id);
21125     }
21126 });
21127 /*
21128  * Based on:
21129  * Ext JS Library 1.1.1
21130  * Copyright(c) 2006-2007, Ext JS, LLC.
21131  *
21132  * Originally Released Under LGPL - original licence link has changed is not relivant.
21133  *
21134  * Fork - LGPL
21135  * <script type="text/javascript">
21136  */
21137  
21138
21139 /**
21140  * @class Roo.dd.ScrollManager
21141  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21142  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21143  * @singleton
21144  */
21145 Roo.dd.ScrollManager = function(){
21146     var ddm = Roo.dd.DragDropMgr;
21147     var els = {};
21148     var dragEl = null;
21149     var proc = {};
21150     
21151     
21152     
21153     var onStop = function(e){
21154         dragEl = null;
21155         clearProc();
21156     };
21157     
21158     var triggerRefresh = function(){
21159         if(ddm.dragCurrent){
21160              ddm.refreshCache(ddm.dragCurrent.groups);
21161         }
21162     };
21163     
21164     var doScroll = function(){
21165         if(ddm.dragCurrent){
21166             var dds = Roo.dd.ScrollManager;
21167             if(!dds.animate){
21168                 if(proc.el.scroll(proc.dir, dds.increment)){
21169                     triggerRefresh();
21170                 }
21171             }else{
21172                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21173             }
21174         }
21175     };
21176     
21177     var clearProc = function(){
21178         if(proc.id){
21179             clearInterval(proc.id);
21180         }
21181         proc.id = 0;
21182         proc.el = null;
21183         proc.dir = "";
21184     };
21185     
21186     var startProc = function(el, dir){
21187          Roo.log('scroll startproc');
21188         clearProc();
21189         proc.el = el;
21190         proc.dir = dir;
21191         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21192     };
21193     
21194     var onFire = function(e, isDrop){
21195        
21196         if(isDrop || !ddm.dragCurrent){ return; }
21197         var dds = Roo.dd.ScrollManager;
21198         if(!dragEl || dragEl != ddm.dragCurrent){
21199             dragEl = ddm.dragCurrent;
21200             // refresh regions on drag start
21201             dds.refreshCache();
21202         }
21203         
21204         var xy = Roo.lib.Event.getXY(e);
21205         var pt = new Roo.lib.Point(xy[0], xy[1]);
21206         for(var id in els){
21207             var el = els[id], r = el._region;
21208             if(r && r.contains(pt) && el.isScrollable()){
21209                 if(r.bottom - pt.y <= dds.thresh){
21210                     if(proc.el != el){
21211                         startProc(el, "down");
21212                     }
21213                     return;
21214                 }else if(r.right - pt.x <= dds.thresh){
21215                     if(proc.el != el){
21216                         startProc(el, "left");
21217                     }
21218                     return;
21219                 }else if(pt.y - r.top <= dds.thresh){
21220                     if(proc.el != el){
21221                         startProc(el, "up");
21222                     }
21223                     return;
21224                 }else if(pt.x - r.left <= dds.thresh){
21225                     if(proc.el != el){
21226                         startProc(el, "right");
21227                     }
21228                     return;
21229                 }
21230             }
21231         }
21232         clearProc();
21233     };
21234     
21235     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21236     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21237     
21238     return {
21239         /**
21240          * Registers new overflow element(s) to auto scroll
21241          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21242          */
21243         register : function(el){
21244             if(el instanceof Array){
21245                 for(var i = 0, len = el.length; i < len; i++) {
21246                         this.register(el[i]);
21247                 }
21248             }else{
21249                 el = Roo.get(el);
21250                 els[el.id] = el;
21251             }
21252             Roo.dd.ScrollManager.els = els;
21253         },
21254         
21255         /**
21256          * Unregisters overflow element(s) so they are no longer scrolled
21257          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21258          */
21259         unregister : function(el){
21260             if(el instanceof Array){
21261                 for(var i = 0, len = el.length; i < len; i++) {
21262                         this.unregister(el[i]);
21263                 }
21264             }else{
21265                 el = Roo.get(el);
21266                 delete els[el.id];
21267             }
21268         },
21269         
21270         /**
21271          * The number of pixels from the edge of a container the pointer needs to be to 
21272          * trigger scrolling (defaults to 25)
21273          * @type Number
21274          */
21275         thresh : 25,
21276         
21277         /**
21278          * The number of pixels to scroll in each scroll increment (defaults to 50)
21279          * @type Number
21280          */
21281         increment : 100,
21282         
21283         /**
21284          * The frequency of scrolls in milliseconds (defaults to 500)
21285          * @type Number
21286          */
21287         frequency : 500,
21288         
21289         /**
21290          * True to animate the scroll (defaults to true)
21291          * @type Boolean
21292          */
21293         animate: true,
21294         
21295         /**
21296          * The animation duration in seconds - 
21297          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21298          * @type Number
21299          */
21300         animDuration: .4,
21301         
21302         /**
21303          * Manually trigger a cache refresh.
21304          */
21305         refreshCache : function(){
21306             for(var id in els){
21307                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21308                     els[id]._region = els[id].getRegion();
21309                 }
21310             }
21311         }
21312     };
21313 }();/*
21314  * Based on:
21315  * Ext JS Library 1.1.1
21316  * Copyright(c) 2006-2007, Ext JS, LLC.
21317  *
21318  * Originally Released Under LGPL - original licence link has changed is not relivant.
21319  *
21320  * Fork - LGPL
21321  * <script type="text/javascript">
21322  */
21323  
21324
21325 /**
21326  * @class Roo.dd.Registry
21327  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21328  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21329  * @singleton
21330  */
21331 Roo.dd.Registry = function(){
21332     var elements = {}; 
21333     var handles = {}; 
21334     var autoIdSeed = 0;
21335
21336     var getId = function(el, autogen){
21337         if(typeof el == "string"){
21338             return el;
21339         }
21340         var id = el.id;
21341         if(!id && autogen !== false){
21342             id = "roodd-" + (++autoIdSeed);
21343             el.id = id;
21344         }
21345         return id;
21346     };
21347     
21348     return {
21349     /**
21350      * Register a drag drop element
21351      * @param {String|HTMLElement} element The id or DOM node to register
21352      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21353      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21354      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21355      * populated in the data object (if applicable):
21356      * <pre>
21357 Value      Description<br />
21358 ---------  ------------------------------------------<br />
21359 handles    Array of DOM nodes that trigger dragging<br />
21360            for the element being registered<br />
21361 isHandle   True if the element passed in triggers<br />
21362            dragging itself, else false
21363 </pre>
21364      */
21365         register : function(el, data){
21366             data = data || {};
21367             if(typeof el == "string"){
21368                 el = document.getElementById(el);
21369             }
21370             data.ddel = el;
21371             elements[getId(el)] = data;
21372             if(data.isHandle !== false){
21373                 handles[data.ddel.id] = data;
21374             }
21375             if(data.handles){
21376                 var hs = data.handles;
21377                 for(var i = 0, len = hs.length; i < len; i++){
21378                         handles[getId(hs[i])] = data;
21379                 }
21380             }
21381         },
21382
21383     /**
21384      * Unregister a drag drop element
21385      * @param {String|HTMLElement}  element The id or DOM node to unregister
21386      */
21387         unregister : function(el){
21388             var id = getId(el, false);
21389             var data = elements[id];
21390             if(data){
21391                 delete elements[id];
21392                 if(data.handles){
21393                     var hs = data.handles;
21394                     for(var i = 0, len = hs.length; i < len; i++){
21395                         delete handles[getId(hs[i], false)];
21396                     }
21397                 }
21398             }
21399         },
21400
21401     /**
21402      * Returns the handle registered for a DOM Node by id
21403      * @param {String|HTMLElement} id The DOM node or id to look up
21404      * @return {Object} handle The custom handle data
21405      */
21406         getHandle : function(id){
21407             if(typeof id != "string"){ // must be element?
21408                 id = id.id;
21409             }
21410             return handles[id];
21411         },
21412
21413     /**
21414      * Returns the handle that is registered for the DOM node that is the target of the event
21415      * @param {Event} e The event
21416      * @return {Object} handle The custom handle data
21417      */
21418         getHandleFromEvent : function(e){
21419             var t = Roo.lib.Event.getTarget(e);
21420             return t ? handles[t.id] : null;
21421         },
21422
21423     /**
21424      * Returns a custom data object that is registered for a DOM node by id
21425      * @param {String|HTMLElement} id The DOM node or id to look up
21426      * @return {Object} data The custom data
21427      */
21428         getTarget : function(id){
21429             if(typeof id != "string"){ // must be element?
21430                 id = id.id;
21431             }
21432             return elements[id];
21433         },
21434
21435     /**
21436      * Returns a custom data object that is registered for the DOM node that is the target of the event
21437      * @param {Event} e The event
21438      * @return {Object} data The custom data
21439      */
21440         getTargetFromEvent : function(e){
21441             var t = Roo.lib.Event.getTarget(e);
21442             return t ? elements[t.id] || handles[t.id] : null;
21443         }
21444     };
21445 }();/*
21446  * Based on:
21447  * Ext JS Library 1.1.1
21448  * Copyright(c) 2006-2007, Ext JS, LLC.
21449  *
21450  * Originally Released Under LGPL - original licence link has changed is not relivant.
21451  *
21452  * Fork - LGPL
21453  * <script type="text/javascript">
21454  */
21455  
21456
21457 /**
21458  * @class Roo.dd.StatusProxy
21459  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21460  * default drag proxy used by all Roo.dd components.
21461  * @constructor
21462  * @param {Object} config
21463  */
21464 Roo.dd.StatusProxy = function(config){
21465     Roo.apply(this, config);
21466     this.id = this.id || Roo.id();
21467     this.el = new Roo.Layer({
21468         dh: {
21469             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21470                 {tag: "div", cls: "x-dd-drop-icon"},
21471                 {tag: "div", cls: "x-dd-drag-ghost"}
21472             ]
21473         }, 
21474         shadow: !config || config.shadow !== false
21475     });
21476     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21477     this.dropStatus = this.dropNotAllowed;
21478 };
21479
21480 Roo.dd.StatusProxy.prototype = {
21481     /**
21482      * @cfg {String} dropAllowed
21483      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21484      */
21485     dropAllowed : "x-dd-drop-ok",
21486     /**
21487      * @cfg {String} dropNotAllowed
21488      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21489      */
21490     dropNotAllowed : "x-dd-drop-nodrop",
21491
21492     /**
21493      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21494      * over the current target element.
21495      * @param {String} cssClass The css class for the new drop status indicator image
21496      */
21497     setStatus : function(cssClass){
21498         cssClass = cssClass || this.dropNotAllowed;
21499         if(this.dropStatus != cssClass){
21500             this.el.replaceClass(this.dropStatus, cssClass);
21501             this.dropStatus = cssClass;
21502         }
21503     },
21504
21505     /**
21506      * Resets the status indicator to the default dropNotAllowed value
21507      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21508      */
21509     reset : function(clearGhost){
21510         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21511         this.dropStatus = this.dropNotAllowed;
21512         if(clearGhost){
21513             this.ghost.update("");
21514         }
21515     },
21516
21517     /**
21518      * Updates the contents of the ghost element
21519      * @param {String} html The html that will replace the current innerHTML of the ghost element
21520      */
21521     update : function(html){
21522         if(typeof html == "string"){
21523             this.ghost.update(html);
21524         }else{
21525             this.ghost.update("");
21526             html.style.margin = "0";
21527             this.ghost.dom.appendChild(html);
21528         }
21529         // ensure float = none set?? cant remember why though.
21530         var el = this.ghost.dom.firstChild;
21531                 if(el){
21532                         Roo.fly(el).setStyle('float', 'none');
21533                 }
21534     },
21535     
21536     /**
21537      * Returns the underlying proxy {@link Roo.Layer}
21538      * @return {Roo.Layer} el
21539     */
21540     getEl : function(){
21541         return this.el;
21542     },
21543
21544     /**
21545      * Returns the ghost element
21546      * @return {Roo.Element} el
21547      */
21548     getGhost : function(){
21549         return this.ghost;
21550     },
21551
21552     /**
21553      * Hides the proxy
21554      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21555      */
21556     hide : function(clear){
21557         this.el.hide();
21558         if(clear){
21559             this.reset(true);
21560         }
21561     },
21562
21563     /**
21564      * Stops the repair animation if it's currently running
21565      */
21566     stop : function(){
21567         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21568             this.anim.stop();
21569         }
21570     },
21571
21572     /**
21573      * Displays this proxy
21574      */
21575     show : function(){
21576         this.el.show();
21577     },
21578
21579     /**
21580      * Force the Layer to sync its shadow and shim positions to the element
21581      */
21582     sync : function(){
21583         this.el.sync();
21584     },
21585
21586     /**
21587      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21588      * invalid drop operation by the item being dragged.
21589      * @param {Array} xy The XY position of the element ([x, y])
21590      * @param {Function} callback The function to call after the repair is complete
21591      * @param {Object} scope The scope in which to execute the callback
21592      */
21593     repair : function(xy, callback, scope){
21594         this.callback = callback;
21595         this.scope = scope;
21596         if(xy && this.animRepair !== false){
21597             this.el.addClass("x-dd-drag-repair");
21598             this.el.hideUnders(true);
21599             this.anim = this.el.shift({
21600                 duration: this.repairDuration || .5,
21601                 easing: 'easeOut',
21602                 xy: xy,
21603                 stopFx: true,
21604                 callback: this.afterRepair,
21605                 scope: this
21606             });
21607         }else{
21608             this.afterRepair();
21609         }
21610     },
21611
21612     // private
21613     afterRepair : function(){
21614         this.hide(true);
21615         if(typeof this.callback == "function"){
21616             this.callback.call(this.scope || this);
21617         }
21618         this.callback = null;
21619         this.scope = null;
21620     }
21621 };/*
21622  * Based on:
21623  * Ext JS Library 1.1.1
21624  * Copyright(c) 2006-2007, Ext JS, LLC.
21625  *
21626  * Originally Released Under LGPL - original licence link has changed is not relivant.
21627  *
21628  * Fork - LGPL
21629  * <script type="text/javascript">
21630  */
21631
21632 /**
21633  * @class Roo.dd.DragSource
21634  * @extends Roo.dd.DDProxy
21635  * A simple class that provides the basic implementation needed to make any element draggable.
21636  * @constructor
21637  * @param {String/HTMLElement/Element} el The container element
21638  * @param {Object} config
21639  */
21640 Roo.dd.DragSource = function(el, config){
21641     this.el = Roo.get(el);
21642     this.dragData = {};
21643     
21644     Roo.apply(this, config);
21645     
21646     if(!this.proxy){
21647         this.proxy = new Roo.dd.StatusProxy();
21648     }
21649
21650     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21651           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21652     
21653     this.dragging = false;
21654 };
21655
21656 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21657     /**
21658      * @cfg {String} dropAllowed
21659      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21660      */
21661     dropAllowed : "x-dd-drop-ok",
21662     /**
21663      * @cfg {String} dropNotAllowed
21664      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21665      */
21666     dropNotAllowed : "x-dd-drop-nodrop",
21667
21668     /**
21669      * Returns the data object associated with this drag source
21670      * @return {Object} data An object containing arbitrary data
21671      */
21672     getDragData : function(e){
21673         return this.dragData;
21674     },
21675
21676     // private
21677     onDragEnter : function(e, id){
21678         var target = Roo.dd.DragDropMgr.getDDById(id);
21679         this.cachedTarget = target;
21680         if(this.beforeDragEnter(target, e, id) !== false){
21681             if(target.isNotifyTarget){
21682                 var status = target.notifyEnter(this, e, this.dragData);
21683                 this.proxy.setStatus(status);
21684             }else{
21685                 this.proxy.setStatus(this.dropAllowed);
21686             }
21687             
21688             if(this.afterDragEnter){
21689                 /**
21690                  * An empty function by default, but provided so that you can perform a custom action
21691                  * when the dragged item enters the drop target by providing an implementation.
21692                  * @param {Roo.dd.DragDrop} target The drop target
21693                  * @param {Event} e The event object
21694                  * @param {String} id The id of the dragged element
21695                  * @method afterDragEnter
21696                  */
21697                 this.afterDragEnter(target, e, id);
21698             }
21699         }
21700     },
21701
21702     /**
21703      * An empty function by default, but provided so that you can perform a custom action
21704      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21705      * @param {Roo.dd.DragDrop} target The drop target
21706      * @param {Event} e The event object
21707      * @param {String} id The id of the dragged element
21708      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21709      */
21710     beforeDragEnter : function(target, e, id){
21711         return true;
21712     },
21713
21714     // private
21715     alignElWithMouse: function() {
21716         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21717         this.proxy.sync();
21718     },
21719
21720     // private
21721     onDragOver : function(e, id){
21722         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21723         if(this.beforeDragOver(target, e, id) !== false){
21724             if(target.isNotifyTarget){
21725                 var status = target.notifyOver(this, e, this.dragData);
21726                 this.proxy.setStatus(status);
21727             }
21728
21729             if(this.afterDragOver){
21730                 /**
21731                  * An empty function by default, but provided so that you can perform a custom action
21732                  * while the dragged item is over the drop target by providing an implementation.
21733                  * @param {Roo.dd.DragDrop} target The drop target
21734                  * @param {Event} e The event object
21735                  * @param {String} id The id of the dragged element
21736                  * @method afterDragOver
21737                  */
21738                 this.afterDragOver(target, e, id);
21739             }
21740         }
21741     },
21742
21743     /**
21744      * An empty function by default, but provided so that you can perform a custom action
21745      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21746      * @param {Roo.dd.DragDrop} target The drop target
21747      * @param {Event} e The event object
21748      * @param {String} id The id of the dragged element
21749      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21750      */
21751     beforeDragOver : function(target, e, id){
21752         return true;
21753     },
21754
21755     // private
21756     onDragOut : function(e, id){
21757         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21758         if(this.beforeDragOut(target, e, id) !== false){
21759             if(target.isNotifyTarget){
21760                 target.notifyOut(this, e, this.dragData);
21761             }
21762             this.proxy.reset();
21763             if(this.afterDragOut){
21764                 /**
21765                  * An empty function by default, but provided so that you can perform a custom action
21766                  * after the dragged item is dragged out of the target without dropping.
21767                  * @param {Roo.dd.DragDrop} target The drop target
21768                  * @param {Event} e The event object
21769                  * @param {String} id The id of the dragged element
21770                  * @method afterDragOut
21771                  */
21772                 this.afterDragOut(target, e, id);
21773             }
21774         }
21775         this.cachedTarget = null;
21776     },
21777
21778     /**
21779      * An empty function by default, but provided so that you can perform a custom action before the dragged
21780      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21781      * @param {Roo.dd.DragDrop} target The drop target
21782      * @param {Event} e The event object
21783      * @param {String} id The id of the dragged element
21784      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21785      */
21786     beforeDragOut : function(target, e, id){
21787         return true;
21788     },
21789     
21790     // private
21791     onDragDrop : function(e, id){
21792         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21793         if(this.beforeDragDrop(target, e, id) !== false){
21794             if(target.isNotifyTarget){
21795                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21796                     this.onValidDrop(target, e, id);
21797                 }else{
21798                     this.onInvalidDrop(target, e, id);
21799                 }
21800             }else{
21801                 this.onValidDrop(target, e, id);
21802             }
21803             
21804             if(this.afterDragDrop){
21805                 /**
21806                  * An empty function by default, but provided so that you can perform a custom action
21807                  * after a valid drag drop has occurred by providing an implementation.
21808                  * @param {Roo.dd.DragDrop} target The drop target
21809                  * @param {Event} e The event object
21810                  * @param {String} id The id of the dropped element
21811                  * @method afterDragDrop
21812                  */
21813                 this.afterDragDrop(target, e, id);
21814             }
21815         }
21816         delete this.cachedTarget;
21817     },
21818
21819     /**
21820      * An empty function by default, but provided so that you can perform a custom action before the dragged
21821      * item is dropped onto the target and optionally cancel the onDragDrop.
21822      * @param {Roo.dd.DragDrop} target The drop target
21823      * @param {Event} e The event object
21824      * @param {String} id The id of the dragged element
21825      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21826      */
21827     beforeDragDrop : function(target, e, id){
21828         return true;
21829     },
21830
21831     // private
21832     onValidDrop : function(target, e, id){
21833         this.hideProxy();
21834         if(this.afterValidDrop){
21835             /**
21836              * An empty function by default, but provided so that you can perform a custom action
21837              * after a valid drop has occurred by providing an implementation.
21838              * @param {Object} target The target DD 
21839              * @param {Event} e The event object
21840              * @param {String} id The id of the dropped element
21841              * @method afterInvalidDrop
21842              */
21843             this.afterValidDrop(target, e, id);
21844         }
21845     },
21846
21847     // private
21848     getRepairXY : function(e, data){
21849         return this.el.getXY();  
21850     },
21851
21852     // private
21853     onInvalidDrop : function(target, e, id){
21854         this.beforeInvalidDrop(target, e, id);
21855         if(this.cachedTarget){
21856             if(this.cachedTarget.isNotifyTarget){
21857                 this.cachedTarget.notifyOut(this, e, this.dragData);
21858             }
21859             this.cacheTarget = null;
21860         }
21861         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21862
21863         if(this.afterInvalidDrop){
21864             /**
21865              * An empty function by default, but provided so that you can perform a custom action
21866              * after an invalid drop has occurred by providing an implementation.
21867              * @param {Event} e The event object
21868              * @param {String} id The id of the dropped element
21869              * @method afterInvalidDrop
21870              */
21871             this.afterInvalidDrop(e, id);
21872         }
21873     },
21874
21875     // private
21876     afterRepair : function(){
21877         if(Roo.enableFx){
21878             this.el.highlight(this.hlColor || "c3daf9");
21879         }
21880         this.dragging = false;
21881     },
21882
21883     /**
21884      * An empty function by default, but provided so that you can perform a custom action after an invalid
21885      * drop has occurred.
21886      * @param {Roo.dd.DragDrop} target The drop target
21887      * @param {Event} e The event object
21888      * @param {String} id The id of the dragged element
21889      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21890      */
21891     beforeInvalidDrop : function(target, e, id){
21892         return true;
21893     },
21894
21895     // private
21896     handleMouseDown : function(e){
21897         if(this.dragging) {
21898             return;
21899         }
21900         var data = this.getDragData(e);
21901         if(data && this.onBeforeDrag(data, e) !== false){
21902             this.dragData = data;
21903             this.proxy.stop();
21904             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21905         } 
21906     },
21907
21908     /**
21909      * An empty function by default, but provided so that you can perform a custom action before the initial
21910      * drag event begins and optionally cancel it.
21911      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21912      * @param {Event} e The event object
21913      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21914      */
21915     onBeforeDrag : function(data, e){
21916         return true;
21917     },
21918
21919     /**
21920      * An empty function by default, but provided so that you can perform a custom action once the initial
21921      * drag event has begun.  The drag cannot be canceled from this function.
21922      * @param {Number} x The x position of the click on the dragged object
21923      * @param {Number} y The y position of the click on the dragged object
21924      */
21925     onStartDrag : Roo.emptyFn,
21926
21927     // private - YUI override
21928     startDrag : function(x, y){
21929         this.proxy.reset();
21930         this.dragging = true;
21931         this.proxy.update("");
21932         this.onInitDrag(x, y);
21933         this.proxy.show();
21934     },
21935
21936     // private
21937     onInitDrag : function(x, y){
21938         var clone = this.el.dom.cloneNode(true);
21939         clone.id = Roo.id(); // prevent duplicate ids
21940         this.proxy.update(clone);
21941         this.onStartDrag(x, y);
21942         return true;
21943     },
21944
21945     /**
21946      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21947      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21948      */
21949     getProxy : function(){
21950         return this.proxy;  
21951     },
21952
21953     /**
21954      * Hides the drag source's {@link Roo.dd.StatusProxy}
21955      */
21956     hideProxy : function(){
21957         this.proxy.hide();  
21958         this.proxy.reset(true);
21959         this.dragging = false;
21960     },
21961
21962     // private
21963     triggerCacheRefresh : function(){
21964         Roo.dd.DDM.refreshCache(this.groups);
21965     },
21966
21967     // private - override to prevent hiding
21968     b4EndDrag: function(e) {
21969     },
21970
21971     // private - override to prevent moving
21972     endDrag : function(e){
21973         this.onEndDrag(this.dragData, e);
21974     },
21975
21976     // private
21977     onEndDrag : function(data, e){
21978     },
21979     
21980     // private - pin to cursor
21981     autoOffset : function(x, y) {
21982         this.setDelta(-12, -20);
21983     }    
21984 });/*
21985  * Based on:
21986  * Ext JS Library 1.1.1
21987  * Copyright(c) 2006-2007, Ext JS, LLC.
21988  *
21989  * Originally Released Under LGPL - original licence link has changed is not relivant.
21990  *
21991  * Fork - LGPL
21992  * <script type="text/javascript">
21993  */
21994
21995
21996 /**
21997  * @class Roo.dd.DropTarget
21998  * @extends Roo.dd.DDTarget
21999  * A simple class that provides the basic implementation needed to make any element a drop target that can have
22000  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
22001  * @constructor
22002  * @param {String/HTMLElement/Element} el The container element
22003  * @param {Object} config
22004  */
22005 Roo.dd.DropTarget = function(el, config){
22006     this.el = Roo.get(el);
22007     
22008     var listeners = false; ;
22009     if (config && config.listeners) {
22010         listeners= config.listeners;
22011         delete config.listeners;
22012     }
22013     Roo.apply(this, config);
22014     
22015     if(this.containerScroll){
22016         Roo.dd.ScrollManager.register(this.el);
22017     }
22018     this.addEvents( {
22019          /**
22020          * @scope Roo.dd.DropTarget
22021          */
22022          
22023          /**
22024          * @event enter
22025          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22026          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22027          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22028          * 
22029          * IMPORTANT : it should set this.overClass and this.dropAllowed
22030          * 
22031          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22032          * @param {Event} e The event
22033          * @param {Object} data An object containing arbitrary data supplied by the drag source
22034          */
22035         "enter" : true,
22036         
22037          /**
22038          * @event over
22039          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22040          * This method will be called on every mouse movement while the drag source is over the drop target.
22041          * This default implementation simply returns the dropAllowed config value.
22042          * 
22043          * IMPORTANT : it should set this.dropAllowed
22044          * 
22045          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22046          * @param {Event} e The event
22047          * @param {Object} data An object containing arbitrary data supplied by the drag source
22048          
22049          */
22050         "over" : true,
22051         /**
22052          * @event out
22053          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22054          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22055          * overClass (if any) from the drop element.
22056          * 
22057          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22058          * @param {Event} e The event
22059          * @param {Object} data An object containing arbitrary data supplied by the drag source
22060          */
22061          "out" : true,
22062          
22063         /**
22064          * @event drop
22065          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22066          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22067          * implementation that does something to process the drop event and returns true so that the drag source's
22068          * repair action does not run.
22069          * 
22070          * IMPORTANT : it should set this.success
22071          * 
22072          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22073          * @param {Event} e The event
22074          * @param {Object} data An object containing arbitrary data supplied by the drag source
22075         */
22076          "drop" : true
22077     });
22078             
22079      
22080     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22081         this.el.dom, 
22082         this.ddGroup || this.group,
22083         {
22084             isTarget: true,
22085             listeners : listeners || {} 
22086            
22087         
22088         }
22089     );
22090
22091 };
22092
22093 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22094     /**
22095      * @cfg {String} overClass
22096      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22097      */
22098      /**
22099      * @cfg {String} ddGroup
22100      * The drag drop group to handle drop events for
22101      */
22102      
22103     /**
22104      * @cfg {String} dropAllowed
22105      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22106      */
22107     dropAllowed : "x-dd-drop-ok",
22108     /**
22109      * @cfg {String} dropNotAllowed
22110      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22111      */
22112     dropNotAllowed : "x-dd-drop-nodrop",
22113     /**
22114      * @cfg {boolean} success
22115      * set this after drop listener.. 
22116      */
22117     success : false,
22118     /**
22119      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22120      * if the drop point is valid for over/enter..
22121      */
22122     valid : false,
22123     // private
22124     isTarget : true,
22125
22126     // private
22127     isNotifyTarget : true,
22128     
22129     /**
22130      * @hide
22131      */
22132     notifyEnter : function(dd, e, data)
22133     {
22134         this.valid = true;
22135         this.fireEvent('enter', dd, e, data);
22136         if(this.overClass){
22137             this.el.addClass(this.overClass);
22138         }
22139         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22140             this.valid ? this.dropAllowed : this.dropNotAllowed
22141         );
22142     },
22143
22144     /**
22145      * @hide
22146      */
22147     notifyOver : function(dd, e, data)
22148     {
22149         this.valid = true;
22150         this.fireEvent('over', dd, e, data);
22151         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22152             this.valid ? this.dropAllowed : this.dropNotAllowed
22153         );
22154     },
22155
22156     /**
22157      * @hide
22158      */
22159     notifyOut : function(dd, e, data)
22160     {
22161         this.fireEvent('out', dd, e, data);
22162         if(this.overClass){
22163             this.el.removeClass(this.overClass);
22164         }
22165     },
22166
22167     /**
22168      * @hide
22169      */
22170     notifyDrop : function(dd, e, data)
22171     {
22172         this.success = false;
22173         this.fireEvent('drop', dd, e, data);
22174         return this.success;
22175     }
22176 });/*
22177  * Based on:
22178  * Ext JS Library 1.1.1
22179  * Copyright(c) 2006-2007, Ext JS, LLC.
22180  *
22181  * Originally Released Under LGPL - original licence link has changed is not relivant.
22182  *
22183  * Fork - LGPL
22184  * <script type="text/javascript">
22185  */
22186
22187
22188 /**
22189  * @class Roo.dd.DragZone
22190  * @extends Roo.dd.DragSource
22191  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22192  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22193  * @constructor
22194  * @param {String/HTMLElement/Element} el The container element
22195  * @param {Object} config
22196  */
22197 Roo.dd.DragZone = function(el, config){
22198     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22199     if(this.containerScroll){
22200         Roo.dd.ScrollManager.register(this.el);
22201     }
22202 };
22203
22204 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22205     /**
22206      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22207      * for auto scrolling during drag operations.
22208      */
22209     /**
22210      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22211      * method after a failed drop (defaults to "c3daf9" - light blue)
22212      */
22213
22214     /**
22215      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22216      * for a valid target to drag based on the mouse down. Override this method
22217      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22218      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22219      * @param {EventObject} e The mouse down event
22220      * @return {Object} The dragData
22221      */
22222     getDragData : function(e){
22223         return Roo.dd.Registry.getHandleFromEvent(e);
22224     },
22225     
22226     /**
22227      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22228      * this.dragData.ddel
22229      * @param {Number} x The x position of the click on the dragged object
22230      * @param {Number} y The y position of the click on the dragged object
22231      * @return {Boolean} true to continue the drag, false to cancel
22232      */
22233     onInitDrag : function(x, y){
22234         this.proxy.update(this.dragData.ddel.cloneNode(true));
22235         this.onStartDrag(x, y);
22236         return true;
22237     },
22238     
22239     /**
22240      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22241      */
22242     afterRepair : function(){
22243         if(Roo.enableFx){
22244             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22245         }
22246         this.dragging = false;
22247     },
22248
22249     /**
22250      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22251      * the XY of this.dragData.ddel
22252      * @param {EventObject} e The mouse up event
22253      * @return {Array} The xy location (e.g. [100, 200])
22254      */
22255     getRepairXY : function(e){
22256         return Roo.Element.fly(this.dragData.ddel).getXY();  
22257     }
22258 });/*
22259  * Based on:
22260  * Ext JS Library 1.1.1
22261  * Copyright(c) 2006-2007, Ext JS, LLC.
22262  *
22263  * Originally Released Under LGPL - original licence link has changed is not relivant.
22264  *
22265  * Fork - LGPL
22266  * <script type="text/javascript">
22267  */
22268 /**
22269  * @class Roo.dd.DropZone
22270  * @extends Roo.dd.DropTarget
22271  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22272  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22273  * @constructor
22274  * @param {String/HTMLElement/Element} el The container element
22275  * @param {Object} config
22276  */
22277 Roo.dd.DropZone = function(el, config){
22278     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22279 };
22280
22281 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22282     /**
22283      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22284      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22285      * provide your own custom lookup.
22286      * @param {Event} e The event
22287      * @return {Object} data The custom data
22288      */
22289     getTargetFromEvent : function(e){
22290         return Roo.dd.Registry.getTargetFromEvent(e);
22291     },
22292
22293     /**
22294      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22295      * that it has registered.  This method has no default implementation and should be overridden to provide
22296      * node-specific processing if necessary.
22297      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22298      * {@link #getTargetFromEvent} for this node)
22299      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22300      * @param {Event} e The event
22301      * @param {Object} data An object containing arbitrary data supplied by the drag source
22302      */
22303     onNodeEnter : function(n, dd, e, data){
22304         
22305     },
22306
22307     /**
22308      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22309      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22310      * overridden to provide the proper feedback.
22311      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22312      * {@link #getTargetFromEvent} for this node)
22313      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22314      * @param {Event} e The event
22315      * @param {Object} data An object containing arbitrary data supplied by the drag source
22316      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22317      * underlying {@link Roo.dd.StatusProxy} can be updated
22318      */
22319     onNodeOver : function(n, dd, e, data){
22320         return this.dropAllowed;
22321     },
22322
22323     /**
22324      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22325      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22326      * node-specific processing if necessary.
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      */
22333     onNodeOut : function(n, dd, e, data){
22334         
22335     },
22336
22337     /**
22338      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22339      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22340      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22341      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22342      * {@link #getTargetFromEvent} for this node)
22343      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22344      * @param {Event} e The event
22345      * @param {Object} data An object containing arbitrary data supplied by the drag source
22346      * @return {Boolean} True if the drop was valid, else false
22347      */
22348     onNodeDrop : function(n, dd, e, data){
22349         return false;
22350     },
22351
22352     /**
22353      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22354      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22355      * it should be overridden to provide the proper feedback if necessary.
22356      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22357      * @param {Event} e The event
22358      * @param {Object} data An object containing arbitrary data supplied by the drag source
22359      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22360      * underlying {@link Roo.dd.StatusProxy} can be updated
22361      */
22362     onContainerOver : function(dd, e, data){
22363         return this.dropNotAllowed;
22364     },
22365
22366     /**
22367      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22368      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22369      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22370      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22371      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22372      * @param {Event} e The event
22373      * @param {Object} data An object containing arbitrary data supplied by the drag source
22374      * @return {Boolean} True if the drop was valid, else false
22375      */
22376     onContainerDrop : function(dd, e, data){
22377         return false;
22378     },
22379
22380     /**
22381      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22382      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22383      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22384      * you should override this method and provide a custom implementation.
22385      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22386      * @param {Event} e The event
22387      * @param {Object} data An object containing arbitrary data supplied by the drag source
22388      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22389      * underlying {@link Roo.dd.StatusProxy} can be updated
22390      */
22391     notifyEnter : function(dd, e, data){
22392         return this.dropNotAllowed;
22393     },
22394
22395     /**
22396      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22397      * This method will be called on every mouse movement while the drag source is over the drop zone.
22398      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22399      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22400      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22401      * registered node, it will call {@link #onContainerOver}.
22402      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22403      * @param {Event} e The event
22404      * @param {Object} data An object containing arbitrary data supplied by the drag source
22405      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22406      * underlying {@link Roo.dd.StatusProxy} can be updated
22407      */
22408     notifyOver : function(dd, e, data){
22409         var n = this.getTargetFromEvent(e);
22410         if(!n){ // not over valid drop target
22411             if(this.lastOverNode){
22412                 this.onNodeOut(this.lastOverNode, dd, e, data);
22413                 this.lastOverNode = null;
22414             }
22415             return this.onContainerOver(dd, e, data);
22416         }
22417         if(this.lastOverNode != n){
22418             if(this.lastOverNode){
22419                 this.onNodeOut(this.lastOverNode, dd, e, data);
22420             }
22421             this.onNodeEnter(n, dd, e, data);
22422             this.lastOverNode = n;
22423         }
22424         return this.onNodeOver(n, dd, e, data);
22425     },
22426
22427     /**
22428      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22429      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22430      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22431      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22432      * @param {Event} e The event
22433      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22434      */
22435     notifyOut : function(dd, e, data){
22436         if(this.lastOverNode){
22437             this.onNodeOut(this.lastOverNode, dd, e, data);
22438             this.lastOverNode = null;
22439         }
22440     },
22441
22442     /**
22443      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22444      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22445      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22446      * otherwise it will call {@link #onContainerDrop}.
22447      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22448      * @param {Event} e The event
22449      * @param {Object} data An object containing arbitrary data supplied by the drag source
22450      * @return {Boolean} True if the drop was valid, else false
22451      */
22452     notifyDrop : function(dd, e, data){
22453         if(this.lastOverNode){
22454             this.onNodeOut(this.lastOverNode, dd, e, data);
22455             this.lastOverNode = null;
22456         }
22457         var n = this.getTargetFromEvent(e);
22458         return n ?
22459             this.onNodeDrop(n, dd, e, data) :
22460             this.onContainerDrop(dd, e, data);
22461     },
22462
22463     // private
22464     triggerCacheRefresh : function(){
22465         Roo.dd.DDM.refreshCache(this.groups);
22466     }  
22467 });/*
22468  * Based on:
22469  * Ext JS Library 1.1.1
22470  * Copyright(c) 2006-2007, Ext JS, LLC.
22471  *
22472  * Originally Released Under LGPL - original licence link has changed is not relivant.
22473  *
22474  * Fork - LGPL
22475  * <script type="text/javascript">
22476  */
22477
22478
22479 /**
22480  * @class Roo.data.SortTypes
22481  * @singleton
22482  * Defines the default sorting (casting?) comparison functions used when sorting data.
22483  */
22484 Roo.data.SortTypes = {
22485     /**
22486      * Default sort that does nothing
22487      * @param {Mixed} s The value being converted
22488      * @return {Mixed} The comparison value
22489      */
22490     none : function(s){
22491         return s;
22492     },
22493     
22494     /**
22495      * The regular expression used to strip tags
22496      * @type {RegExp}
22497      * @property
22498      */
22499     stripTagsRE : /<\/?[^>]+>/gi,
22500     
22501     /**
22502      * Strips all HTML tags to sort on text only
22503      * @param {Mixed} s The value being converted
22504      * @return {String} The comparison value
22505      */
22506     asText : function(s){
22507         return String(s).replace(this.stripTagsRE, "");
22508     },
22509     
22510     /**
22511      * Strips all HTML tags to sort on text only - Case insensitive
22512      * @param {Mixed} s The value being converted
22513      * @return {String} The comparison value
22514      */
22515     asUCText : function(s){
22516         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22517     },
22518     
22519     /**
22520      * Case insensitive string
22521      * @param {Mixed} s The value being converted
22522      * @return {String} The comparison value
22523      */
22524     asUCString : function(s) {
22525         return String(s).toUpperCase();
22526     },
22527     
22528     /**
22529      * Date sorting
22530      * @param {Mixed} s The value being converted
22531      * @return {Number} The comparison value
22532      */
22533     asDate : function(s) {
22534         if(!s){
22535             return 0;
22536         }
22537         if(s instanceof Date){
22538             return s.getTime();
22539         }
22540         return Date.parse(String(s));
22541     },
22542     
22543     /**
22544      * Float sorting
22545      * @param {Mixed} s The value being converted
22546      * @return {Float} The comparison value
22547      */
22548     asFloat : function(s) {
22549         var val = parseFloat(String(s).replace(/,/g, ""));
22550         if(isNaN(val)) {
22551             val = 0;
22552         }
22553         return val;
22554     },
22555     
22556     /**
22557      * Integer sorting
22558      * @param {Mixed} s The value being converted
22559      * @return {Number} The comparison value
22560      */
22561     asInt : function(s) {
22562         var val = parseInt(String(s).replace(/,/g, ""));
22563         if(isNaN(val)) {
22564             val = 0;
22565         }
22566         return val;
22567     }
22568 };/*
22569  * Based on:
22570  * Ext JS Library 1.1.1
22571  * Copyright(c) 2006-2007, Ext JS, LLC.
22572  *
22573  * Originally Released Under LGPL - original licence link has changed is not relivant.
22574  *
22575  * Fork - LGPL
22576  * <script type="text/javascript">
22577  */
22578
22579 /**
22580 * @class Roo.data.Record
22581  * Instances of this class encapsulate both record <em>definition</em> information, and record
22582  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22583  * to access Records cached in an {@link Roo.data.Store} object.<br>
22584  * <p>
22585  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22586  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22587  * objects.<br>
22588  * <p>
22589  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22590  * @constructor
22591  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22592  * {@link #create}. The parameters are the same.
22593  * @param {Array} data An associative Array of data values keyed by the field name.
22594  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22595  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22596  * not specified an integer id is generated.
22597  */
22598 Roo.data.Record = function(data, id){
22599     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22600     this.data = data;
22601 };
22602
22603 /**
22604  * Generate a constructor for a specific record layout.
22605  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22606  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22607  * Each field definition object may contain the following properties: <ul>
22608  * <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,
22609  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22610  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22611  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22612  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22613  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22614  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22615  * this may be omitted.</p></li>
22616  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22617  * <ul><li>auto (Default, implies no conversion)</li>
22618  * <li>string</li>
22619  * <li>int</li>
22620  * <li>float</li>
22621  * <li>boolean</li>
22622  * <li>date</li></ul></p></li>
22623  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22624  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22625  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22626  * by the Reader into an object that will be stored in the Record. It is passed the
22627  * following parameters:<ul>
22628  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22629  * </ul></p></li>
22630  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22631  * </ul>
22632  * <br>usage:<br><pre><code>
22633 var TopicRecord = Roo.data.Record.create(
22634     {name: 'title', mapping: 'topic_title'},
22635     {name: 'author', mapping: 'username'},
22636     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22637     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22638     {name: 'lastPoster', mapping: 'user2'},
22639     {name: 'excerpt', mapping: 'post_text'}
22640 );
22641
22642 var myNewRecord = new TopicRecord({
22643     title: 'Do my job please',
22644     author: 'noobie',
22645     totalPosts: 1,
22646     lastPost: new Date(),
22647     lastPoster: 'Animal',
22648     excerpt: 'No way dude!'
22649 });
22650 myStore.add(myNewRecord);
22651 </code></pre>
22652  * @method create
22653  * @static
22654  */
22655 Roo.data.Record.create = function(o){
22656     var f = function(){
22657         f.superclass.constructor.apply(this, arguments);
22658     };
22659     Roo.extend(f, Roo.data.Record);
22660     var p = f.prototype;
22661     p.fields = new Roo.util.MixedCollection(false, function(field){
22662         return field.name;
22663     });
22664     for(var i = 0, len = o.length; i < len; i++){
22665         p.fields.add(new Roo.data.Field(o[i]));
22666     }
22667     f.getField = function(name){
22668         return p.fields.get(name);  
22669     };
22670     return f;
22671 };
22672
22673 Roo.data.Record.AUTO_ID = 1000;
22674 Roo.data.Record.EDIT = 'edit';
22675 Roo.data.Record.REJECT = 'reject';
22676 Roo.data.Record.COMMIT = 'commit';
22677
22678 Roo.data.Record.prototype = {
22679     /**
22680      * Readonly flag - true if this record has been modified.
22681      * @type Boolean
22682      */
22683     dirty : false,
22684     editing : false,
22685     error: null,
22686     modified: null,
22687
22688     // private
22689     join : function(store){
22690         this.store = store;
22691     },
22692
22693     /**
22694      * Set the named field to the specified value.
22695      * @param {String} name The name of the field to set.
22696      * @param {Object} value The value to set the field to.
22697      */
22698     set : function(name, value){
22699         if(this.data[name] == value){
22700             return;
22701         }
22702         this.dirty = true;
22703         if(!this.modified){
22704             this.modified = {};
22705         }
22706         if(typeof this.modified[name] == 'undefined'){
22707             this.modified[name] = this.data[name];
22708         }
22709         this.data[name] = value;
22710         if(!this.editing && this.store){
22711             this.store.afterEdit(this);
22712         }       
22713     },
22714
22715     /**
22716      * Get the value of the named field.
22717      * @param {String} name The name of the field to get the value of.
22718      * @return {Object} The value of the field.
22719      */
22720     get : function(name){
22721         return this.data[name]; 
22722     },
22723
22724     // private
22725     beginEdit : function(){
22726         this.editing = true;
22727         this.modified = {}; 
22728     },
22729
22730     // private
22731     cancelEdit : function(){
22732         this.editing = false;
22733         delete this.modified;
22734     },
22735
22736     // private
22737     endEdit : function(){
22738         this.editing = false;
22739         if(this.dirty && this.store){
22740             this.store.afterEdit(this);
22741         }
22742     },
22743
22744     /**
22745      * Usually called by the {@link Roo.data.Store} which owns the Record.
22746      * Rejects all changes made to the Record since either creation, or the last commit operation.
22747      * Modified fields are reverted to their original values.
22748      * <p>
22749      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22750      * of reject operations.
22751      */
22752     reject : function(){
22753         var m = this.modified;
22754         for(var n in m){
22755             if(typeof m[n] != "function"){
22756                 this.data[n] = m[n];
22757             }
22758         }
22759         this.dirty = false;
22760         delete this.modified;
22761         this.editing = false;
22762         if(this.store){
22763             this.store.afterReject(this);
22764         }
22765     },
22766
22767     /**
22768      * Usually called by the {@link Roo.data.Store} which owns the Record.
22769      * Commits all changes made to the Record since either creation, or the last commit operation.
22770      * <p>
22771      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22772      * of commit operations.
22773      */
22774     commit : function(){
22775         this.dirty = false;
22776         delete this.modified;
22777         this.editing = false;
22778         if(this.store){
22779             this.store.afterCommit(this);
22780         }
22781     },
22782
22783     // private
22784     hasError : function(){
22785         return this.error != null;
22786     },
22787
22788     // private
22789     clearError : function(){
22790         this.error = null;
22791     },
22792
22793     /**
22794      * Creates a copy of this record.
22795      * @param {String} id (optional) A new record id if you don't want to use this record's id
22796      * @return {Record}
22797      */
22798     copy : function(newId) {
22799         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22800     }
22801 };/*
22802  * Based on:
22803  * Ext JS Library 1.1.1
22804  * Copyright(c) 2006-2007, Ext JS, LLC.
22805  *
22806  * Originally Released Under LGPL - original licence link has changed is not relivant.
22807  *
22808  * Fork - LGPL
22809  * <script type="text/javascript">
22810  */
22811
22812
22813
22814 /**
22815  * @class Roo.data.Store
22816  * @extends Roo.util.Observable
22817  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22818  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22819  * <p>
22820  * 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
22821  * has no knowledge of the format of the data returned by the Proxy.<br>
22822  * <p>
22823  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22824  * instances from the data object. These records are cached and made available through accessor functions.
22825  * @constructor
22826  * Creates a new Store.
22827  * @param {Object} config A config object containing the objects needed for the Store to access data,
22828  * and read the data into Records.
22829  */
22830 Roo.data.Store = function(config){
22831     this.data = new Roo.util.MixedCollection(false);
22832     this.data.getKey = function(o){
22833         return o.id;
22834     };
22835     this.baseParams = {};
22836     // private
22837     this.paramNames = {
22838         "start" : "start",
22839         "limit" : "limit",
22840         "sort" : "sort",
22841         "dir" : "dir",
22842         "multisort" : "_multisort"
22843     };
22844
22845     if(config && config.data){
22846         this.inlineData = config.data;
22847         delete config.data;
22848     }
22849
22850     Roo.apply(this, config);
22851     
22852     if(this.reader){ // reader passed
22853         this.reader = Roo.factory(this.reader, Roo.data);
22854         this.reader.xmodule = this.xmodule || false;
22855         if(!this.recordType){
22856             this.recordType = this.reader.recordType;
22857         }
22858         if(this.reader.onMetaChange){
22859             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22860         }
22861     }
22862
22863     if(this.recordType){
22864         this.fields = this.recordType.prototype.fields;
22865     }
22866     this.modified = [];
22867
22868     this.addEvents({
22869         /**
22870          * @event datachanged
22871          * Fires when the data cache has changed, and a widget which is using this Store
22872          * as a Record cache should refresh its view.
22873          * @param {Store} this
22874          */
22875         datachanged : true,
22876         /**
22877          * @event metachange
22878          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22879          * @param {Store} this
22880          * @param {Object} meta The JSON metadata
22881          */
22882         metachange : true,
22883         /**
22884          * @event add
22885          * Fires when Records have been added to the Store
22886          * @param {Store} this
22887          * @param {Roo.data.Record[]} records The array of Records added
22888          * @param {Number} index The index at which the record(s) were added
22889          */
22890         add : true,
22891         /**
22892          * @event remove
22893          * Fires when a Record has been removed from the Store
22894          * @param {Store} this
22895          * @param {Roo.data.Record} record The Record that was removed
22896          * @param {Number} index The index at which the record was removed
22897          */
22898         remove : true,
22899         /**
22900          * @event update
22901          * Fires when a Record has been updated
22902          * @param {Store} this
22903          * @param {Roo.data.Record} record The Record that was updated
22904          * @param {String} operation The update operation being performed.  Value may be one of:
22905          * <pre><code>
22906  Roo.data.Record.EDIT
22907  Roo.data.Record.REJECT
22908  Roo.data.Record.COMMIT
22909          * </code></pre>
22910          */
22911         update : true,
22912         /**
22913          * @event clear
22914          * Fires when the data cache has been cleared.
22915          * @param {Store} this
22916          */
22917         clear : true,
22918         /**
22919          * @event beforeload
22920          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22921          * the load action will be canceled.
22922          * @param {Store} this
22923          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22924          */
22925         beforeload : true,
22926         /**
22927          * @event beforeloadadd
22928          * Fires after a new set of Records has been loaded.
22929          * @param {Store} this
22930          * @param {Roo.data.Record[]} records The Records that were loaded
22931          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22932          */
22933         beforeloadadd : true,
22934         /**
22935          * @event load
22936          * Fires after a new set of Records has been loaded, before they are added to the store.
22937          * @param {Store} this
22938          * @param {Roo.data.Record[]} records The Records that were loaded
22939          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22940          * @params {Object} return from reader
22941          */
22942         load : true,
22943         /**
22944          * @event loadexception
22945          * Fires if an exception occurs in the Proxy during loading.
22946          * Called with the signature of the Proxy's "loadexception" event.
22947          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22948          * 
22949          * @param {Proxy} 
22950          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22951          * @param {Object} load options 
22952          * @param {Object} jsonData from your request (normally this contains the Exception)
22953          */
22954         loadexception : true
22955     });
22956     
22957     if(this.proxy){
22958         this.proxy = Roo.factory(this.proxy, Roo.data);
22959         this.proxy.xmodule = this.xmodule || false;
22960         this.relayEvents(this.proxy,  ["loadexception"]);
22961     }
22962     this.sortToggle = {};
22963     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22964
22965     Roo.data.Store.superclass.constructor.call(this);
22966
22967     if(this.inlineData){
22968         this.loadData(this.inlineData);
22969         delete this.inlineData;
22970     }
22971 };
22972
22973 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22974      /**
22975     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22976     * without a remote query - used by combo/forms at present.
22977     */
22978     
22979     /**
22980     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22981     */
22982     /**
22983     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22984     */
22985     /**
22986     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22987     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22988     */
22989     /**
22990     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22991     * on any HTTP request
22992     */
22993     /**
22994     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22995     */
22996     /**
22997     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22998     */
22999     multiSort: false,
23000     /**
23001     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
23002     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
23003     */
23004     remoteSort : false,
23005
23006     /**
23007     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23008      * loaded or when a record is removed. (defaults to false).
23009     */
23010     pruneModifiedRecords : false,
23011
23012     // private
23013     lastOptions : null,
23014
23015     /**
23016      * Add Records to the Store and fires the add event.
23017      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23018      */
23019     add : function(records){
23020         records = [].concat(records);
23021         for(var i = 0, len = records.length; i < len; i++){
23022             records[i].join(this);
23023         }
23024         var index = this.data.length;
23025         this.data.addAll(records);
23026         this.fireEvent("add", this, records, index);
23027     },
23028
23029     /**
23030      * Remove a Record from the Store and fires the remove event.
23031      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23032      */
23033     remove : function(record){
23034         var index = this.data.indexOf(record);
23035         this.data.removeAt(index);
23036  
23037         if(this.pruneModifiedRecords){
23038             this.modified.remove(record);
23039         }
23040         this.fireEvent("remove", this, record, index);
23041     },
23042
23043     /**
23044      * Remove all Records from the Store and fires the clear event.
23045      */
23046     removeAll : function(){
23047         this.data.clear();
23048         if(this.pruneModifiedRecords){
23049             this.modified = [];
23050         }
23051         this.fireEvent("clear", this);
23052     },
23053
23054     /**
23055      * Inserts Records to the Store at the given index and fires the add event.
23056      * @param {Number} index The start index at which to insert the passed Records.
23057      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23058      */
23059     insert : function(index, records){
23060         records = [].concat(records);
23061         for(var i = 0, len = records.length; i < len; i++){
23062             this.data.insert(index, records[i]);
23063             records[i].join(this);
23064         }
23065         this.fireEvent("add", this, records, index);
23066     },
23067
23068     /**
23069      * Get the index within the cache of the passed Record.
23070      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23071      * @return {Number} The index of the passed Record. Returns -1 if not found.
23072      */
23073     indexOf : function(record){
23074         return this.data.indexOf(record);
23075     },
23076
23077     /**
23078      * Get the index within the cache of the Record with the passed id.
23079      * @param {String} id The id of the Record to find.
23080      * @return {Number} The index of the Record. Returns -1 if not found.
23081      */
23082     indexOfId : function(id){
23083         return this.data.indexOfKey(id);
23084     },
23085
23086     /**
23087      * Get the Record with the specified id.
23088      * @param {String} id The id of the Record to find.
23089      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23090      */
23091     getById : function(id){
23092         return this.data.key(id);
23093     },
23094
23095     /**
23096      * Get the Record at the specified index.
23097      * @param {Number} index The index of the Record to find.
23098      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23099      */
23100     getAt : function(index){
23101         return this.data.itemAt(index);
23102     },
23103
23104     /**
23105      * Returns a range of Records between specified indices.
23106      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23107      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23108      * @return {Roo.data.Record[]} An array of Records
23109      */
23110     getRange : function(start, end){
23111         return this.data.getRange(start, end);
23112     },
23113
23114     // private
23115     storeOptions : function(o){
23116         o = Roo.apply({}, o);
23117         delete o.callback;
23118         delete o.scope;
23119         this.lastOptions = o;
23120     },
23121
23122     /**
23123      * Loads the Record cache from the configured Proxy using the configured Reader.
23124      * <p>
23125      * If using remote paging, then the first load call must specify the <em>start</em>
23126      * and <em>limit</em> properties in the options.params property to establish the initial
23127      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23128      * <p>
23129      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23130      * and this call will return before the new data has been loaded. Perform any post-processing
23131      * in a callback function, or in a "load" event handler.</strong>
23132      * <p>
23133      * @param {Object} options An object containing properties which control loading options:<ul>
23134      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23135      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23136      * passed the following arguments:<ul>
23137      * <li>r : Roo.data.Record[]</li>
23138      * <li>options: Options object from the load call</li>
23139      * <li>success: Boolean success indicator</li></ul></li>
23140      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23141      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23142      * </ul>
23143      */
23144     load : function(options){
23145         options = options || {};
23146         if(this.fireEvent("beforeload", this, options) !== false){
23147             this.storeOptions(options);
23148             var p = Roo.apply(options.params || {}, this.baseParams);
23149             // if meta was not loaded from remote source.. try requesting it.
23150             if (!this.reader.metaFromRemote) {
23151                 p._requestMeta = 1;
23152             }
23153             if(this.sortInfo && this.remoteSort){
23154                 var pn = this.paramNames;
23155                 p[pn["sort"]] = this.sortInfo.field;
23156                 p[pn["dir"]] = this.sortInfo.direction;
23157             }
23158             if (this.multiSort) {
23159                 var pn = this.paramNames;
23160                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23161             }
23162             
23163             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23164         }
23165     },
23166
23167     /**
23168      * Reloads the Record cache from the configured Proxy using the configured Reader and
23169      * the options from the last load operation performed.
23170      * @param {Object} options (optional) An object containing properties which may override the options
23171      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23172      * the most recently used options are reused).
23173      */
23174     reload : function(options){
23175         this.load(Roo.applyIf(options||{}, this.lastOptions));
23176     },
23177
23178     // private
23179     // Called as a callback by the Reader during a load operation.
23180     loadRecords : function(o, options, success){
23181         if(!o || success === false){
23182             if(success !== false){
23183                 this.fireEvent("load", this, [], options, o);
23184             }
23185             if(options.callback){
23186                 options.callback.call(options.scope || this, [], options, false);
23187             }
23188             return;
23189         }
23190         // if data returned failure - throw an exception.
23191         if (o.success === false) {
23192             // show a message if no listener is registered.
23193             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23194                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23195             }
23196             // loadmask wil be hooked into this..
23197             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23198             return;
23199         }
23200         var r = o.records, t = o.totalRecords || r.length;
23201         
23202         this.fireEvent("beforeloadadd", this, r, options, o);
23203         
23204         if(!options || options.add !== true){
23205             if(this.pruneModifiedRecords){
23206                 this.modified = [];
23207             }
23208             for(var i = 0, len = r.length; i < len; i++){
23209                 r[i].join(this);
23210             }
23211             if(this.snapshot){
23212                 this.data = this.snapshot;
23213                 delete this.snapshot;
23214             }
23215             this.data.clear();
23216             this.data.addAll(r);
23217             this.totalLength = t;
23218             this.applySort();
23219             this.fireEvent("datachanged", this);
23220         }else{
23221             this.totalLength = Math.max(t, this.data.length+r.length);
23222             this.add(r);
23223         }
23224         
23225         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23226                 
23227             var e = new Roo.data.Record({});
23228
23229             e.set(this.parent.displayField, this.parent.emptyTitle);
23230             e.set(this.parent.valueField, '');
23231
23232             this.insert(0, e);
23233         }
23234             
23235         this.fireEvent("load", this, r, options, o);
23236         if(options.callback){
23237             options.callback.call(options.scope || this, r, options, true);
23238         }
23239     },
23240
23241
23242     /**
23243      * Loads data from a passed data block. A Reader which understands the format of the data
23244      * must have been configured in the constructor.
23245      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23246      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23247      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23248      */
23249     loadData : function(o, append){
23250         var r = this.reader.readRecords(o);
23251         this.loadRecords(r, {add: append}, true);
23252     },
23253
23254     /**
23255      * Gets the number of cached records.
23256      * <p>
23257      * <em>If using paging, this may not be the total size of the dataset. If the data object
23258      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23259      * the data set size</em>
23260      */
23261     getCount : function(){
23262         return this.data.length || 0;
23263     },
23264
23265     /**
23266      * Gets the total number of records in the dataset as returned by the server.
23267      * <p>
23268      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23269      * the dataset size</em>
23270      */
23271     getTotalCount : function(){
23272         return this.totalLength || 0;
23273     },
23274
23275     /**
23276      * Returns the sort state of the Store as an object with two properties:
23277      * <pre><code>
23278  field {String} The name of the field by which the Records are sorted
23279  direction {String} The sort order, "ASC" or "DESC"
23280      * </code></pre>
23281      */
23282     getSortState : function(){
23283         return this.sortInfo;
23284     },
23285
23286     // private
23287     applySort : function(){
23288         if(this.sortInfo && !this.remoteSort){
23289             var s = this.sortInfo, f = s.field;
23290             var st = this.fields.get(f).sortType;
23291             var fn = function(r1, r2){
23292                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23293                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23294             };
23295             this.data.sort(s.direction, fn);
23296             if(this.snapshot && this.snapshot != this.data){
23297                 this.snapshot.sort(s.direction, fn);
23298             }
23299         }
23300     },
23301
23302     /**
23303      * Sets the default sort column and order to be used by the next load operation.
23304      * @param {String} fieldName The name of the field to sort by.
23305      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23306      */
23307     setDefaultSort : function(field, dir){
23308         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23309     },
23310
23311     /**
23312      * Sort the Records.
23313      * If remote sorting is used, the sort is performed on the server, and the cache is
23314      * reloaded. If local sorting is used, the cache is sorted internally.
23315      * @param {String} fieldName The name of the field to sort by.
23316      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23317      */
23318     sort : function(fieldName, dir){
23319         var f = this.fields.get(fieldName);
23320         if(!dir){
23321             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23322             
23323             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23324                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23325             }else{
23326                 dir = f.sortDir;
23327             }
23328         }
23329         this.sortToggle[f.name] = dir;
23330         this.sortInfo = {field: f.name, direction: dir};
23331         if(!this.remoteSort){
23332             this.applySort();
23333             this.fireEvent("datachanged", this);
23334         }else{
23335             this.load(this.lastOptions);
23336         }
23337     },
23338
23339     /**
23340      * Calls the specified function for each of the Records in the cache.
23341      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23342      * Returning <em>false</em> aborts and exits the iteration.
23343      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23344      */
23345     each : function(fn, scope){
23346         this.data.each(fn, scope);
23347     },
23348
23349     /**
23350      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23351      * (e.g., during paging).
23352      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23353      */
23354     getModifiedRecords : function(){
23355         return this.modified;
23356     },
23357
23358     // private
23359     createFilterFn : function(property, value, anyMatch){
23360         if(!value.exec){ // not a regex
23361             value = String(value);
23362             if(value.length == 0){
23363                 return false;
23364             }
23365             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23366         }
23367         return function(r){
23368             return value.test(r.data[property]);
23369         };
23370     },
23371
23372     /**
23373      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23374      * @param {String} property A field on your records
23375      * @param {Number} start The record index to start at (defaults to 0)
23376      * @param {Number} end The last record index to include (defaults to length - 1)
23377      * @return {Number} The sum
23378      */
23379     sum : function(property, start, end){
23380         var rs = this.data.items, v = 0;
23381         start = start || 0;
23382         end = (end || end === 0) ? end : rs.length-1;
23383
23384         for(var i = start; i <= end; i++){
23385             v += (rs[i].data[property] || 0);
23386         }
23387         return v;
23388     },
23389
23390     /**
23391      * Filter the records by a specified property.
23392      * @param {String} field A field on your records
23393      * @param {String/RegExp} value Either a string that the field
23394      * should start with or a RegExp to test against the field
23395      * @param {Boolean} anyMatch True to match any part not just the beginning
23396      */
23397     filter : function(property, value, anyMatch){
23398         var fn = this.createFilterFn(property, value, anyMatch);
23399         return fn ? this.filterBy(fn) : this.clearFilter();
23400     },
23401
23402     /**
23403      * Filter by a function. The specified function will be called with each
23404      * record in this data source. If the function returns true the record is included,
23405      * otherwise it is filtered.
23406      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23407      * @param {Object} scope (optional) The scope of the function (defaults to this)
23408      */
23409     filterBy : function(fn, scope){
23410         this.snapshot = this.snapshot || this.data;
23411         this.data = this.queryBy(fn, scope||this);
23412         this.fireEvent("datachanged", this);
23413     },
23414
23415     /**
23416      * Query the records by a specified property.
23417      * @param {String} field A field on your records
23418      * @param {String/RegExp} value Either a string that the field
23419      * should start with or a RegExp to test against the field
23420      * @param {Boolean} anyMatch True to match any part not just the beginning
23421      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23422      */
23423     query : function(property, value, anyMatch){
23424         var fn = this.createFilterFn(property, value, anyMatch);
23425         return fn ? this.queryBy(fn) : this.data.clone();
23426     },
23427
23428     /**
23429      * Query by a function. The specified function will be called with each
23430      * record in this data source. If the function returns true the record is included
23431      * in the results.
23432      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23433      * @param {Object} scope (optional) The scope of the function (defaults to this)
23434       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23435      **/
23436     queryBy : function(fn, scope){
23437         var data = this.snapshot || this.data;
23438         return data.filterBy(fn, scope||this);
23439     },
23440
23441     /**
23442      * Collects unique values for a particular dataIndex from this store.
23443      * @param {String} dataIndex The property to collect
23444      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23445      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23446      * @return {Array} An array of the unique values
23447      **/
23448     collect : function(dataIndex, allowNull, bypassFilter){
23449         var d = (bypassFilter === true && this.snapshot) ?
23450                 this.snapshot.items : this.data.items;
23451         var v, sv, r = [], l = {};
23452         for(var i = 0, len = d.length; i < len; i++){
23453             v = d[i].data[dataIndex];
23454             sv = String(v);
23455             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23456                 l[sv] = true;
23457                 r[r.length] = v;
23458             }
23459         }
23460         return r;
23461     },
23462
23463     /**
23464      * Revert to a view of the Record cache with no filtering applied.
23465      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23466      */
23467     clearFilter : function(suppressEvent){
23468         if(this.snapshot && this.snapshot != this.data){
23469             this.data = this.snapshot;
23470             delete this.snapshot;
23471             if(suppressEvent !== true){
23472                 this.fireEvent("datachanged", this);
23473             }
23474         }
23475     },
23476
23477     // private
23478     afterEdit : function(record){
23479         if(this.modified.indexOf(record) == -1){
23480             this.modified.push(record);
23481         }
23482         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23483     },
23484     
23485     // private
23486     afterReject : function(record){
23487         this.modified.remove(record);
23488         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23489     },
23490
23491     // private
23492     afterCommit : function(record){
23493         this.modified.remove(record);
23494         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23495     },
23496
23497     /**
23498      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23499      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23500      */
23501     commitChanges : function(){
23502         var m = this.modified.slice(0);
23503         this.modified = [];
23504         for(var i = 0, len = m.length; i < len; i++){
23505             m[i].commit();
23506         }
23507     },
23508
23509     /**
23510      * Cancel outstanding changes on all changed records.
23511      */
23512     rejectChanges : function(){
23513         var m = this.modified.slice(0);
23514         this.modified = [];
23515         for(var i = 0, len = m.length; i < len; i++){
23516             m[i].reject();
23517         }
23518     },
23519
23520     onMetaChange : function(meta, rtype, o){
23521         this.recordType = rtype;
23522         this.fields = rtype.prototype.fields;
23523         delete this.snapshot;
23524         this.sortInfo = meta.sortInfo || this.sortInfo;
23525         this.modified = [];
23526         this.fireEvent('metachange', this, this.reader.meta);
23527     },
23528     
23529     moveIndex : function(data, type)
23530     {
23531         var index = this.indexOf(data);
23532         
23533         var newIndex = index + type;
23534         
23535         this.remove(data);
23536         
23537         this.insert(newIndex, data);
23538         
23539     }
23540 });/*
23541  * Based on:
23542  * Ext JS Library 1.1.1
23543  * Copyright(c) 2006-2007, Ext JS, LLC.
23544  *
23545  * Originally Released Under LGPL - original licence link has changed is not relivant.
23546  *
23547  * Fork - LGPL
23548  * <script type="text/javascript">
23549  */
23550
23551 /**
23552  * @class Roo.data.SimpleStore
23553  * @extends Roo.data.Store
23554  * Small helper class to make creating Stores from Array data easier.
23555  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23556  * @cfg {Array} fields An array of field definition objects, or field name strings.
23557  * @cfg {Object} an existing reader (eg. copied from another store)
23558  * @cfg {Array} data The multi-dimensional array of data
23559  * @constructor
23560  * @param {Object} config
23561  */
23562 Roo.data.SimpleStore = function(config)
23563 {
23564     Roo.data.SimpleStore.superclass.constructor.call(this, {
23565         isLocal : true,
23566         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
23567                 id: config.id
23568             },
23569             Roo.data.Record.create(config.fields)
23570         ),
23571         proxy : new Roo.data.MemoryProxy(config.data)
23572     });
23573     this.load();
23574 };
23575 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23576  * Based on:
23577  * Ext JS Library 1.1.1
23578  * Copyright(c) 2006-2007, Ext JS, LLC.
23579  *
23580  * Originally Released Under LGPL - original licence link has changed is not relivant.
23581  *
23582  * Fork - LGPL
23583  * <script type="text/javascript">
23584  */
23585
23586 /**
23587 /**
23588  * @extends Roo.data.Store
23589  * @class Roo.data.JsonStore
23590  * Small helper class to make creating Stores for JSON data easier. <br/>
23591 <pre><code>
23592 var store = new Roo.data.JsonStore({
23593     url: 'get-images.php',
23594     root: 'images',
23595     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23596 });
23597 </code></pre>
23598  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23599  * JsonReader and HttpProxy (unless inline data is provided).</b>
23600  * @cfg {Array} fields An array of field definition objects, or field name strings.
23601  * @constructor
23602  * @param {Object} config
23603  */
23604 Roo.data.JsonStore = function(c){
23605     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23606         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23607         reader: new Roo.data.JsonReader(c, c.fields)
23608     }));
23609 };
23610 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23611  * Based on:
23612  * Ext JS Library 1.1.1
23613  * Copyright(c) 2006-2007, Ext JS, LLC.
23614  *
23615  * Originally Released Under LGPL - original licence link has changed is not relivant.
23616  *
23617  * Fork - LGPL
23618  * <script type="text/javascript">
23619  */
23620
23621  
23622 Roo.data.Field = function(config){
23623     if(typeof config == "string"){
23624         config = {name: config};
23625     }
23626     Roo.apply(this, config);
23627     
23628     if(!this.type){
23629         this.type = "auto";
23630     }
23631     
23632     var st = Roo.data.SortTypes;
23633     // named sortTypes are supported, here we look them up
23634     if(typeof this.sortType == "string"){
23635         this.sortType = st[this.sortType];
23636     }
23637     
23638     // set default sortType for strings and dates
23639     if(!this.sortType){
23640         switch(this.type){
23641             case "string":
23642                 this.sortType = st.asUCString;
23643                 break;
23644             case "date":
23645                 this.sortType = st.asDate;
23646                 break;
23647             default:
23648                 this.sortType = st.none;
23649         }
23650     }
23651
23652     // define once
23653     var stripRe = /[\$,%]/g;
23654
23655     // prebuilt conversion function for this field, instead of
23656     // switching every time we're reading a value
23657     if(!this.convert){
23658         var cv, dateFormat = this.dateFormat;
23659         switch(this.type){
23660             case "":
23661             case "auto":
23662             case undefined:
23663                 cv = function(v){ return v; };
23664                 break;
23665             case "string":
23666                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23667                 break;
23668             case "int":
23669                 cv = function(v){
23670                     return v !== undefined && v !== null && v !== '' ?
23671                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23672                     };
23673                 break;
23674             case "float":
23675                 cv = function(v){
23676                     return v !== undefined && v !== null && v !== '' ?
23677                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23678                     };
23679                 break;
23680             case "bool":
23681             case "boolean":
23682                 cv = function(v){ return v === true || v === "true" || v == 1; };
23683                 break;
23684             case "date":
23685                 cv = function(v){
23686                     if(!v){
23687                         return '';
23688                     }
23689                     if(v instanceof Date){
23690                         return v;
23691                     }
23692                     if(dateFormat){
23693                         if(dateFormat == "timestamp"){
23694                             return new Date(v*1000);
23695                         }
23696                         return Date.parseDate(v, dateFormat);
23697                     }
23698                     var parsed = Date.parse(v);
23699                     return parsed ? new Date(parsed) : null;
23700                 };
23701              break;
23702             
23703         }
23704         this.convert = cv;
23705     }
23706 };
23707
23708 Roo.data.Field.prototype = {
23709     dateFormat: null,
23710     defaultValue: "",
23711     mapping: null,
23712     sortType : null,
23713     sortDir : "ASC"
23714 };/*
23715  * Based on:
23716  * Ext JS Library 1.1.1
23717  * Copyright(c) 2006-2007, Ext JS, LLC.
23718  *
23719  * Originally Released Under LGPL - original licence link has changed is not relivant.
23720  *
23721  * Fork - LGPL
23722  * <script type="text/javascript">
23723  */
23724  
23725 // Base class for reading structured data from a data source.  This class is intended to be
23726 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23727
23728 /**
23729  * @class Roo.data.DataReader
23730  * Base class for reading structured data from a data source.  This class is intended to be
23731  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23732  */
23733
23734 Roo.data.DataReader = function(meta, recordType){
23735     
23736     this.meta = meta;
23737     
23738     this.recordType = recordType instanceof Array ? 
23739         Roo.data.Record.create(recordType) : recordType;
23740 };
23741
23742 Roo.data.DataReader.prototype = {
23743      /**
23744      * Create an empty record
23745      * @param {Object} data (optional) - overlay some values
23746      * @return {Roo.data.Record} record created.
23747      */
23748     newRow :  function(d) {
23749         var da =  {};
23750         this.recordType.prototype.fields.each(function(c) {
23751             switch( c.type) {
23752                 case 'int' : da[c.name] = 0; break;
23753                 case 'date' : da[c.name] = new Date(); break;
23754                 case 'float' : da[c.name] = 0.0; break;
23755                 case 'boolean' : da[c.name] = false; break;
23756                 default : da[c.name] = ""; break;
23757             }
23758             
23759         });
23760         return new this.recordType(Roo.apply(da, d));
23761     }
23762     
23763 };/*
23764  * Based on:
23765  * Ext JS Library 1.1.1
23766  * Copyright(c) 2006-2007, Ext JS, LLC.
23767  *
23768  * Originally Released Under LGPL - original licence link has changed is not relivant.
23769  *
23770  * Fork - LGPL
23771  * <script type="text/javascript">
23772  */
23773
23774 /**
23775  * @class Roo.data.DataProxy
23776  * @extends Roo.data.Observable
23777  * This class is an abstract base class for implementations which provide retrieval of
23778  * unformatted data objects.<br>
23779  * <p>
23780  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23781  * (of the appropriate type which knows how to parse the data object) to provide a block of
23782  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23783  * <p>
23784  * Custom implementations must implement the load method as described in
23785  * {@link Roo.data.HttpProxy#load}.
23786  */
23787 Roo.data.DataProxy = function(){
23788     this.addEvents({
23789         /**
23790          * @event beforeload
23791          * Fires before a network request is made to retrieve a data object.
23792          * @param {Object} This DataProxy object.
23793          * @param {Object} params The params parameter to the load function.
23794          */
23795         beforeload : true,
23796         /**
23797          * @event load
23798          * Fires before the load method's callback is called.
23799          * @param {Object} This DataProxy object.
23800          * @param {Object} o The data object.
23801          * @param {Object} arg The callback argument object passed to the load function.
23802          */
23803         load : true,
23804         /**
23805          * @event loadexception
23806          * Fires if an Exception occurs during data retrieval.
23807          * @param {Object} This DataProxy object.
23808          * @param {Object} o The data object.
23809          * @param {Object} arg The callback argument object passed to the load function.
23810          * @param {Object} e The Exception.
23811          */
23812         loadexception : true
23813     });
23814     Roo.data.DataProxy.superclass.constructor.call(this);
23815 };
23816
23817 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23818
23819     /**
23820      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23821      */
23822 /*
23823  * Based on:
23824  * Ext JS Library 1.1.1
23825  * Copyright(c) 2006-2007, Ext JS, LLC.
23826  *
23827  * Originally Released Under LGPL - original licence link has changed is not relivant.
23828  *
23829  * Fork - LGPL
23830  * <script type="text/javascript">
23831  */
23832 /**
23833  * @class Roo.data.MemoryProxy
23834  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23835  * to the Reader when its load method is called.
23836  * @constructor
23837  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23838  */
23839 Roo.data.MemoryProxy = function(data){
23840     if (data.data) {
23841         data = data.data;
23842     }
23843     Roo.data.MemoryProxy.superclass.constructor.call(this);
23844     this.data = data;
23845 };
23846
23847 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23848     
23849     /**
23850      * Load data from the requested source (in this case an in-memory
23851      * data object passed to the constructor), read the data object into
23852      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23853      * process that block using the passed callback.
23854      * @param {Object} params This parameter is not used by the MemoryProxy class.
23855      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23856      * object into a block of Roo.data.Records.
23857      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23858      * The function must be passed <ul>
23859      * <li>The Record block object</li>
23860      * <li>The "arg" argument from the load function</li>
23861      * <li>A boolean success indicator</li>
23862      * </ul>
23863      * @param {Object} scope The scope in which to call the callback
23864      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23865      */
23866     load : function(params, reader, callback, scope, arg){
23867         params = params || {};
23868         var result;
23869         try {
23870             result = reader.readRecords(params.data ? params.data :this.data);
23871         }catch(e){
23872             this.fireEvent("loadexception", this, arg, null, e);
23873             callback.call(scope, null, arg, false);
23874             return;
23875         }
23876         callback.call(scope, result, arg, true);
23877     },
23878     
23879     // private
23880     update : function(params, records){
23881         
23882     }
23883 });/*
23884  * Based on:
23885  * Ext JS Library 1.1.1
23886  * Copyright(c) 2006-2007, Ext JS, LLC.
23887  *
23888  * Originally Released Under LGPL - original licence link has changed is not relivant.
23889  *
23890  * Fork - LGPL
23891  * <script type="text/javascript">
23892  */
23893 /**
23894  * @class Roo.data.HttpProxy
23895  * @extends Roo.data.DataProxy
23896  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23897  * configured to reference a certain URL.<br><br>
23898  * <p>
23899  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23900  * from which the running page was served.<br><br>
23901  * <p>
23902  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23903  * <p>
23904  * Be aware that to enable the browser to parse an XML document, the server must set
23905  * the Content-Type header in the HTTP response to "text/xml".
23906  * @constructor
23907  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23908  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23909  * will be used to make the request.
23910  */
23911 Roo.data.HttpProxy = function(conn){
23912     Roo.data.HttpProxy.superclass.constructor.call(this);
23913     // is conn a conn config or a real conn?
23914     this.conn = conn;
23915     this.useAjax = !conn || !conn.events;
23916   
23917 };
23918
23919 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23920     // thse are take from connection...
23921     
23922     /**
23923      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23924      */
23925     /**
23926      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23927      * extra parameters to each request made by this object. (defaults to undefined)
23928      */
23929     /**
23930      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23931      *  to each request made by this object. (defaults to undefined)
23932      */
23933     /**
23934      * @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)
23935      */
23936     /**
23937      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23938      */
23939      /**
23940      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23941      * @type Boolean
23942      */
23943   
23944
23945     /**
23946      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23947      * @type Boolean
23948      */
23949     /**
23950      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23951      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23952      * a finer-grained basis than the DataProxy events.
23953      */
23954     getConnection : function(){
23955         return this.useAjax ? Roo.Ajax : this.conn;
23956     },
23957
23958     /**
23959      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23960      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23961      * process that block using the passed callback.
23962      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23963      * for the request to the remote server.
23964      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23965      * object into a block of Roo.data.Records.
23966      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23967      * The function must be passed <ul>
23968      * <li>The Record block object</li>
23969      * <li>The "arg" argument from the load function</li>
23970      * <li>A boolean success indicator</li>
23971      * </ul>
23972      * @param {Object} scope The scope in which to call the callback
23973      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23974      */
23975     load : function(params, reader, callback, scope, arg){
23976         if(this.fireEvent("beforeload", this, params) !== false){
23977             var  o = {
23978                 params : params || {},
23979                 request: {
23980                     callback : callback,
23981                     scope : scope,
23982                     arg : arg
23983                 },
23984                 reader: reader,
23985                 callback : this.loadResponse,
23986                 scope: this
23987             };
23988             if(this.useAjax){
23989                 Roo.applyIf(o, this.conn);
23990                 if(this.activeRequest){
23991                     Roo.Ajax.abort(this.activeRequest);
23992                 }
23993                 this.activeRequest = Roo.Ajax.request(o);
23994             }else{
23995                 this.conn.request(o);
23996             }
23997         }else{
23998             callback.call(scope||this, null, arg, false);
23999         }
24000     },
24001
24002     // private
24003     loadResponse : function(o, success, response){
24004         delete this.activeRequest;
24005         if(!success){
24006             this.fireEvent("loadexception", this, o, response);
24007             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24008             return;
24009         }
24010         var result;
24011         try {
24012             result = o.reader.read(response);
24013         }catch(e){
24014             this.fireEvent("loadexception", this, o, response, e);
24015             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24016             return;
24017         }
24018         
24019         this.fireEvent("load", this, o, o.request.arg);
24020         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24021     },
24022
24023     // private
24024     update : function(dataSet){
24025
24026     },
24027
24028     // private
24029     updateResponse : function(dataSet){
24030
24031     }
24032 });/*
24033  * Based on:
24034  * Ext JS Library 1.1.1
24035  * Copyright(c) 2006-2007, Ext JS, LLC.
24036  *
24037  * Originally Released Under LGPL - original licence link has changed is not relivant.
24038  *
24039  * Fork - LGPL
24040  * <script type="text/javascript">
24041  */
24042
24043 /**
24044  * @class Roo.data.ScriptTagProxy
24045  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24046  * other than the originating domain of the running page.<br><br>
24047  * <p>
24048  * <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
24049  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24050  * <p>
24051  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24052  * source code that is used as the source inside a &lt;script> tag.<br><br>
24053  * <p>
24054  * In order for the browser to process the returned data, the server must wrap the data object
24055  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24056  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24057  * depending on whether the callback name was passed:
24058  * <p>
24059  * <pre><code>
24060 boolean scriptTag = false;
24061 String cb = request.getParameter("callback");
24062 if (cb != null) {
24063     scriptTag = true;
24064     response.setContentType("text/javascript");
24065 } else {
24066     response.setContentType("application/x-json");
24067 }
24068 Writer out = response.getWriter();
24069 if (scriptTag) {
24070     out.write(cb + "(");
24071 }
24072 out.print(dataBlock.toJsonString());
24073 if (scriptTag) {
24074     out.write(");");
24075 }
24076 </pre></code>
24077  *
24078  * @constructor
24079  * @param {Object} config A configuration object.
24080  */
24081 Roo.data.ScriptTagProxy = function(config){
24082     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24083     Roo.apply(this, config);
24084     this.head = document.getElementsByTagName("head")[0];
24085 };
24086
24087 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24088
24089 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24090     /**
24091      * @cfg {String} url The URL from which to request the data object.
24092      */
24093     /**
24094      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24095      */
24096     timeout : 30000,
24097     /**
24098      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24099      * the server the name of the callback function set up by the load call to process the returned data object.
24100      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24101      * javascript output which calls this named function passing the data object as its only parameter.
24102      */
24103     callbackParam : "callback",
24104     /**
24105      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24106      * name to the request.
24107      */
24108     nocache : true,
24109
24110     /**
24111      * Load data from the configured URL, read the data object into
24112      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24113      * process that block using the passed callback.
24114      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24115      * for the request to the remote server.
24116      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24117      * object into a block of Roo.data.Records.
24118      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24119      * The function must be passed <ul>
24120      * <li>The Record block object</li>
24121      * <li>The "arg" argument from the load function</li>
24122      * <li>A boolean success indicator</li>
24123      * </ul>
24124      * @param {Object} scope The scope in which to call the callback
24125      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24126      */
24127     load : function(params, reader, callback, scope, arg){
24128         if(this.fireEvent("beforeload", this, params) !== false){
24129
24130             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24131
24132             var url = this.url;
24133             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24134             if(this.nocache){
24135                 url += "&_dc=" + (new Date().getTime());
24136             }
24137             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24138             var trans = {
24139                 id : transId,
24140                 cb : "stcCallback"+transId,
24141                 scriptId : "stcScript"+transId,
24142                 params : params,
24143                 arg : arg,
24144                 url : url,
24145                 callback : callback,
24146                 scope : scope,
24147                 reader : reader
24148             };
24149             var conn = this;
24150
24151             window[trans.cb] = function(o){
24152                 conn.handleResponse(o, trans);
24153             };
24154
24155             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24156
24157             if(this.autoAbort !== false){
24158                 this.abort();
24159             }
24160
24161             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24162
24163             var script = document.createElement("script");
24164             script.setAttribute("src", url);
24165             script.setAttribute("type", "text/javascript");
24166             script.setAttribute("id", trans.scriptId);
24167             this.head.appendChild(script);
24168
24169             this.trans = trans;
24170         }else{
24171             callback.call(scope||this, null, arg, false);
24172         }
24173     },
24174
24175     // private
24176     isLoading : function(){
24177         return this.trans ? true : false;
24178     },
24179
24180     /**
24181      * Abort the current server request.
24182      */
24183     abort : function(){
24184         if(this.isLoading()){
24185             this.destroyTrans(this.trans);
24186         }
24187     },
24188
24189     // private
24190     destroyTrans : function(trans, isLoaded){
24191         this.head.removeChild(document.getElementById(trans.scriptId));
24192         clearTimeout(trans.timeoutId);
24193         if(isLoaded){
24194             window[trans.cb] = undefined;
24195             try{
24196                 delete window[trans.cb];
24197             }catch(e){}
24198         }else{
24199             // if hasn't been loaded, wait for load to remove it to prevent script error
24200             window[trans.cb] = function(){
24201                 window[trans.cb] = undefined;
24202                 try{
24203                     delete window[trans.cb];
24204                 }catch(e){}
24205             };
24206         }
24207     },
24208
24209     // private
24210     handleResponse : function(o, trans){
24211         this.trans = false;
24212         this.destroyTrans(trans, true);
24213         var result;
24214         try {
24215             result = trans.reader.readRecords(o);
24216         }catch(e){
24217             this.fireEvent("loadexception", this, o, trans.arg, e);
24218             trans.callback.call(trans.scope||window, null, trans.arg, false);
24219             return;
24220         }
24221         this.fireEvent("load", this, o, trans.arg);
24222         trans.callback.call(trans.scope||window, result, trans.arg, true);
24223     },
24224
24225     // private
24226     handleFailure : function(trans){
24227         this.trans = false;
24228         this.destroyTrans(trans, false);
24229         this.fireEvent("loadexception", this, null, trans.arg);
24230         trans.callback.call(trans.scope||window, null, trans.arg, false);
24231     }
24232 });/*
24233  * Based on:
24234  * Ext JS Library 1.1.1
24235  * Copyright(c) 2006-2007, Ext JS, LLC.
24236  *
24237  * Originally Released Under LGPL - original licence link has changed is not relivant.
24238  *
24239  * Fork - LGPL
24240  * <script type="text/javascript">
24241  */
24242
24243 /**
24244  * @class Roo.data.JsonReader
24245  * @extends Roo.data.DataReader
24246  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24247  * based on mappings in a provided Roo.data.Record constructor.
24248  * 
24249  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24250  * in the reply previously. 
24251  * 
24252  * <p>
24253  * Example code:
24254  * <pre><code>
24255 var RecordDef = Roo.data.Record.create([
24256     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24257     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24258 ]);
24259 var myReader = new Roo.data.JsonReader({
24260     totalProperty: "results",    // The property which contains the total dataset size (optional)
24261     root: "rows",                // The property which contains an Array of row objects
24262     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24263 }, RecordDef);
24264 </code></pre>
24265  * <p>
24266  * This would consume a JSON file like this:
24267  * <pre><code>
24268 { 'results': 2, 'rows': [
24269     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24270     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24271 }
24272 </code></pre>
24273  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24274  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24275  * paged from the remote server.
24276  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24277  * @cfg {String} root name of the property which contains the Array of row objects.
24278  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24279  * @cfg {Array} fields Array of field definition objects
24280  * @constructor
24281  * Create a new JsonReader
24282  * @param {Object} meta Metadata configuration options
24283  * @param {Object} recordType Either an Array of field definition objects,
24284  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24285  */
24286 Roo.data.JsonReader = function(meta, recordType){
24287     
24288     meta = meta || {};
24289     // set some defaults:
24290     Roo.applyIf(meta, {
24291         totalProperty: 'total',
24292         successProperty : 'success',
24293         root : 'data',
24294         id : 'id'
24295     });
24296     
24297     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24298 };
24299 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24300     
24301     /**
24302      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24303      * Used by Store query builder to append _requestMeta to params.
24304      * 
24305      */
24306     metaFromRemote : false,
24307     /**
24308      * This method is only used by a DataProxy which has retrieved data from a remote server.
24309      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24310      * @return {Object} data A data block which is used by an Roo.data.Store object as
24311      * a cache of Roo.data.Records.
24312      */
24313     read : function(response){
24314         var json = response.responseText;
24315        
24316         var o = /* eval:var:o */ eval("("+json+")");
24317         if(!o) {
24318             throw {message: "JsonReader.read: Json object not found"};
24319         }
24320         
24321         if(o.metaData){
24322             
24323             delete this.ef;
24324             this.metaFromRemote = true;
24325             this.meta = o.metaData;
24326             this.recordType = Roo.data.Record.create(o.metaData.fields);
24327             this.onMetaChange(this.meta, this.recordType, o);
24328         }
24329         return this.readRecords(o);
24330     },
24331
24332     // private function a store will implement
24333     onMetaChange : function(meta, recordType, o){
24334
24335     },
24336
24337     /**
24338          * @ignore
24339          */
24340     simpleAccess: function(obj, subsc) {
24341         return obj[subsc];
24342     },
24343
24344         /**
24345          * @ignore
24346          */
24347     getJsonAccessor: function(){
24348         var re = /[\[\.]/;
24349         return function(expr) {
24350             try {
24351                 return(re.test(expr))
24352                     ? new Function("obj", "return obj." + expr)
24353                     : function(obj){
24354                         return obj[expr];
24355                     };
24356             } catch(e){}
24357             return Roo.emptyFn;
24358         };
24359     }(),
24360
24361     /**
24362      * Create a data block containing Roo.data.Records from an XML document.
24363      * @param {Object} o An object which contains an Array of row objects in the property specified
24364      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24365      * which contains the total size of the dataset.
24366      * @return {Object} data A data block which is used by an Roo.data.Store object as
24367      * a cache of Roo.data.Records.
24368      */
24369     readRecords : function(o){
24370         /**
24371          * After any data loads, the raw JSON data is available for further custom processing.
24372          * @type Object
24373          */
24374         this.o = o;
24375         var s = this.meta, Record = this.recordType,
24376             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24377
24378 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24379         if (!this.ef) {
24380             if(s.totalProperty) {
24381                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24382                 }
24383                 if(s.successProperty) {
24384                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24385                 }
24386                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24387                 if (s.id) {
24388                         var g = this.getJsonAccessor(s.id);
24389                         this.getId = function(rec) {
24390                                 var r = g(rec);  
24391                                 return (r === undefined || r === "") ? null : r;
24392                         };
24393                 } else {
24394                         this.getId = function(){return null;};
24395                 }
24396             this.ef = [];
24397             for(var jj = 0; jj < fl; jj++){
24398                 f = fi[jj];
24399                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24400                 this.ef[jj] = this.getJsonAccessor(map);
24401             }
24402         }
24403
24404         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24405         if(s.totalProperty){
24406             var vt = parseInt(this.getTotal(o), 10);
24407             if(!isNaN(vt)){
24408                 totalRecords = vt;
24409             }
24410         }
24411         if(s.successProperty){
24412             var vs = this.getSuccess(o);
24413             if(vs === false || vs === 'false'){
24414                 success = false;
24415             }
24416         }
24417         var records = [];
24418         for(var i = 0; i < c; i++){
24419                 var n = root[i];
24420             var values = {};
24421             var id = this.getId(n);
24422             for(var j = 0; j < fl; j++){
24423                 f = fi[j];
24424             var v = this.ef[j](n);
24425             if (!f.convert) {
24426                 Roo.log('missing convert for ' + f.name);
24427                 Roo.log(f);
24428                 continue;
24429             }
24430             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24431             }
24432             var record = new Record(values, id);
24433             record.json = n;
24434             records[i] = record;
24435         }
24436         return {
24437             raw : o,
24438             success : success,
24439             records : records,
24440             totalRecords : totalRecords
24441         };
24442     }
24443 });/*
24444  * Based on:
24445  * Ext JS Library 1.1.1
24446  * Copyright(c) 2006-2007, Ext JS, LLC.
24447  *
24448  * Originally Released Under LGPL - original licence link has changed is not relivant.
24449  *
24450  * Fork - LGPL
24451  * <script type="text/javascript">
24452  */
24453
24454 /**
24455  * @class Roo.data.XmlReader
24456  * @extends Roo.data.DataReader
24457  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24458  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24459  * <p>
24460  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24461  * header in the HTTP response must be set to "text/xml".</em>
24462  * <p>
24463  * Example code:
24464  * <pre><code>
24465 var RecordDef = Roo.data.Record.create([
24466    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24467    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24468 ]);
24469 var myReader = new Roo.data.XmlReader({
24470    totalRecords: "results", // The element which contains the total dataset size (optional)
24471    record: "row",           // The repeated element which contains row information
24472    id: "id"                 // The element within the row that provides an ID for the record (optional)
24473 }, RecordDef);
24474 </code></pre>
24475  * <p>
24476  * This would consume an XML file like this:
24477  * <pre><code>
24478 &lt;?xml?>
24479 &lt;dataset>
24480  &lt;results>2&lt;/results>
24481  &lt;row>
24482    &lt;id>1&lt;/id>
24483    &lt;name>Bill&lt;/name>
24484    &lt;occupation>Gardener&lt;/occupation>
24485  &lt;/row>
24486  &lt;row>
24487    &lt;id>2&lt;/id>
24488    &lt;name>Ben&lt;/name>
24489    &lt;occupation>Horticulturalist&lt;/occupation>
24490  &lt;/row>
24491 &lt;/dataset>
24492 </code></pre>
24493  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24494  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24495  * paged from the remote server.
24496  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24497  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24498  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24499  * a record identifier value.
24500  * @constructor
24501  * Create a new XmlReader
24502  * @param {Object} meta Metadata configuration options
24503  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24504  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24505  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24506  */
24507 Roo.data.XmlReader = function(meta, recordType){
24508     meta = meta || {};
24509     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24510 };
24511 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24512     /**
24513      * This method is only used by a DataProxy which has retrieved data from a remote server.
24514          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24515          * to contain a method called 'responseXML' that returns an XML document object.
24516      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24517      * a cache of Roo.data.Records.
24518      */
24519     read : function(response){
24520         var doc = response.responseXML;
24521         if(!doc) {
24522             throw {message: "XmlReader.read: XML Document not available"};
24523         }
24524         return this.readRecords(doc);
24525     },
24526
24527     /**
24528      * Create a data block containing Roo.data.Records from an XML document.
24529          * @param {Object} doc A parsed XML document.
24530      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24531      * a cache of Roo.data.Records.
24532      */
24533     readRecords : function(doc){
24534         /**
24535          * After any data loads/reads, the raw XML Document is available for further custom processing.
24536          * @type XMLDocument
24537          */
24538         this.xmlData = doc;
24539         var root = doc.documentElement || doc;
24540         var q = Roo.DomQuery;
24541         var recordType = this.recordType, fields = recordType.prototype.fields;
24542         var sid = this.meta.id;
24543         var totalRecords = 0, success = true;
24544         if(this.meta.totalRecords){
24545             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24546         }
24547         
24548         if(this.meta.success){
24549             var sv = q.selectValue(this.meta.success, root, true);
24550             success = sv !== false && sv !== 'false';
24551         }
24552         var records = [];
24553         var ns = q.select(this.meta.record, root);
24554         for(var i = 0, len = ns.length; i < len; i++) {
24555                 var n = ns[i];
24556                 var values = {};
24557                 var id = sid ? q.selectValue(sid, n) : undefined;
24558                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24559                     var f = fields.items[j];
24560                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24561                     v = f.convert(v);
24562                     values[f.name] = v;
24563                 }
24564                 var record = new recordType(values, id);
24565                 record.node = n;
24566                 records[records.length] = record;
24567             }
24568
24569             return {
24570                 success : success,
24571                 records : records,
24572                 totalRecords : totalRecords || records.length
24573             };
24574     }
24575 });/*
24576  * Based on:
24577  * Ext JS Library 1.1.1
24578  * Copyright(c) 2006-2007, Ext JS, LLC.
24579  *
24580  * Originally Released Under LGPL - original licence link has changed is not relivant.
24581  *
24582  * Fork - LGPL
24583  * <script type="text/javascript">
24584  */
24585
24586 /**
24587  * @class Roo.data.ArrayReader
24588  * @extends Roo.data.DataReader
24589  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24590  * Each element of that Array represents a row of data fields. The
24591  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24592  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24593  * <p>
24594  * Example code:.
24595  * <pre><code>
24596 var RecordDef = Roo.data.Record.create([
24597     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24598     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24599 ]);
24600 var myReader = new Roo.data.ArrayReader({
24601     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24602 }, RecordDef);
24603 </code></pre>
24604  * <p>
24605  * This would consume an Array like this:
24606  * <pre><code>
24607 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24608   </code></pre>
24609  
24610  * @constructor
24611  * Create a new JsonReader
24612  * @param {Object} meta Metadata configuration options.
24613  * @param {Object|Array} recordType Either an Array of field definition objects
24614  * 
24615  * @cfg {Array} fields Array of field definition objects
24616  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24617  * as specified to {@link Roo.data.Record#create},
24618  * or an {@link Roo.data.Record} object
24619  *
24620  * 
24621  * created using {@link Roo.data.Record#create}.
24622  */
24623 Roo.data.ArrayReader = function(meta, recordType){
24624     
24625      
24626     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24627 };
24628
24629 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24630     /**
24631      * Create a data block containing Roo.data.Records from an XML document.
24632      * @param {Object} o An Array of row objects which represents the dataset.
24633      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
24634      * a cache of Roo.data.Records.
24635      */
24636     readRecords : function(o)
24637     {
24638         var sid = this.meta ? this.meta.id : null;
24639         var recordType = this.recordType, fields = recordType.prototype.fields;
24640         var records = [];
24641         var root = o;
24642         for(var i = 0; i < root.length; i++){
24643                 var n = root[i];
24644             var values = {};
24645             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24646             for(var j = 0, jlen = fields.length; j < jlen; j++){
24647                 var f = fields.items[j];
24648                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24649                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24650                 v = f.convert(v);
24651                 values[f.name] = v;
24652             }
24653             var record = new recordType(values, id);
24654             record.json = n;
24655             records[records.length] = record;
24656         }
24657         return {
24658             records : records,
24659             totalRecords : records.length
24660         };
24661     }
24662 });/*
24663  * Based on:
24664  * Ext JS Library 1.1.1
24665  * Copyright(c) 2006-2007, Ext JS, LLC.
24666  *
24667  * Originally Released Under LGPL - original licence link has changed is not relivant.
24668  *
24669  * Fork - LGPL
24670  * <script type="text/javascript">
24671  */
24672
24673
24674 /**
24675  * @class Roo.data.Tree
24676  * @extends Roo.util.Observable
24677  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24678  * in the tree have most standard DOM functionality.
24679  * @constructor
24680  * @param {Node} root (optional) The root node
24681  */
24682 Roo.data.Tree = function(root){
24683    this.nodeHash = {};
24684    /**
24685     * The root node for this tree
24686     * @type Node
24687     */
24688    this.root = null;
24689    if(root){
24690        this.setRootNode(root);
24691    }
24692    this.addEvents({
24693        /**
24694         * @event append
24695         * Fires when a new child node is appended to a node in this tree.
24696         * @param {Tree} tree The owner tree
24697         * @param {Node} parent The parent node
24698         * @param {Node} node The newly appended node
24699         * @param {Number} index The index of the newly appended node
24700         */
24701        "append" : true,
24702        /**
24703         * @event remove
24704         * Fires when a child node is removed from a node in this tree.
24705         * @param {Tree} tree The owner tree
24706         * @param {Node} parent The parent node
24707         * @param {Node} node The child node removed
24708         */
24709        "remove" : true,
24710        /**
24711         * @event move
24712         * Fires when a node is moved to a new location in the tree
24713         * @param {Tree} tree The owner tree
24714         * @param {Node} node The node moved
24715         * @param {Node} oldParent The old parent of this node
24716         * @param {Node} newParent The new parent of this node
24717         * @param {Number} index The index it was moved to
24718         */
24719        "move" : true,
24720        /**
24721         * @event insert
24722         * Fires when a new child node is inserted in a node in this tree.
24723         * @param {Tree} tree The owner tree
24724         * @param {Node} parent The parent node
24725         * @param {Node} node The child node inserted
24726         * @param {Node} refNode The child node the node was inserted before
24727         */
24728        "insert" : true,
24729        /**
24730         * @event beforeappend
24731         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24732         * @param {Tree} tree The owner tree
24733         * @param {Node} parent The parent node
24734         * @param {Node} node The child node to be appended
24735         */
24736        "beforeappend" : true,
24737        /**
24738         * @event beforeremove
24739         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24740         * @param {Tree} tree The owner tree
24741         * @param {Node} parent The parent node
24742         * @param {Node} node The child node to be removed
24743         */
24744        "beforeremove" : true,
24745        /**
24746         * @event beforemove
24747         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24748         * @param {Tree} tree The owner tree
24749         * @param {Node} node The node being moved
24750         * @param {Node} oldParent The parent of the node
24751         * @param {Node} newParent The new parent the node is moving to
24752         * @param {Number} index The index it is being moved to
24753         */
24754        "beforemove" : true,
24755        /**
24756         * @event beforeinsert
24757         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24758         * @param {Tree} tree The owner tree
24759         * @param {Node} parent The parent node
24760         * @param {Node} node The child node to be inserted
24761         * @param {Node} refNode The child node the node is being inserted before
24762         */
24763        "beforeinsert" : true
24764    });
24765
24766     Roo.data.Tree.superclass.constructor.call(this);
24767 };
24768
24769 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24770     pathSeparator: "/",
24771
24772     proxyNodeEvent : function(){
24773         return this.fireEvent.apply(this, arguments);
24774     },
24775
24776     /**
24777      * Returns the root node for this tree.
24778      * @return {Node}
24779      */
24780     getRootNode : function(){
24781         return this.root;
24782     },
24783
24784     /**
24785      * Sets the root node for this tree.
24786      * @param {Node} node
24787      * @return {Node}
24788      */
24789     setRootNode : function(node){
24790         this.root = node;
24791         node.ownerTree = this;
24792         node.isRoot = true;
24793         this.registerNode(node);
24794         return node;
24795     },
24796
24797     /**
24798      * Gets a node in this tree by its id.
24799      * @param {String} id
24800      * @return {Node}
24801      */
24802     getNodeById : function(id){
24803         return this.nodeHash[id];
24804     },
24805
24806     registerNode : function(node){
24807         this.nodeHash[node.id] = node;
24808     },
24809
24810     unregisterNode : function(node){
24811         delete this.nodeHash[node.id];
24812     },
24813
24814     toString : function(){
24815         return "[Tree"+(this.id?" "+this.id:"")+"]";
24816     }
24817 });
24818
24819 /**
24820  * @class Roo.data.Node
24821  * @extends Roo.util.Observable
24822  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24823  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24824  * @constructor
24825  * @param {Object} attributes The attributes/config for the node
24826  */
24827 Roo.data.Node = function(attributes){
24828     /**
24829      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24830      * @type {Object}
24831      */
24832     this.attributes = attributes || {};
24833     this.leaf = this.attributes.leaf;
24834     /**
24835      * The node id. @type String
24836      */
24837     this.id = this.attributes.id;
24838     if(!this.id){
24839         this.id = Roo.id(null, "ynode-");
24840         this.attributes.id = this.id;
24841     }
24842      
24843     
24844     /**
24845      * All child nodes of this node. @type Array
24846      */
24847     this.childNodes = [];
24848     if(!this.childNodes.indexOf){ // indexOf is a must
24849         this.childNodes.indexOf = function(o){
24850             for(var i = 0, len = this.length; i < len; i++){
24851                 if(this[i] == o) {
24852                     return i;
24853                 }
24854             }
24855             return -1;
24856         };
24857     }
24858     /**
24859      * The parent node for this node. @type Node
24860      */
24861     this.parentNode = null;
24862     /**
24863      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24864      */
24865     this.firstChild = null;
24866     /**
24867      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24868      */
24869     this.lastChild = null;
24870     /**
24871      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24872      */
24873     this.previousSibling = null;
24874     /**
24875      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24876      */
24877     this.nextSibling = null;
24878
24879     this.addEvents({
24880        /**
24881         * @event append
24882         * Fires when a new child node is appended
24883         * @param {Tree} tree The owner tree
24884         * @param {Node} this This node
24885         * @param {Node} node The newly appended node
24886         * @param {Number} index The index of the newly appended node
24887         */
24888        "append" : true,
24889        /**
24890         * @event remove
24891         * Fires when a child node is removed
24892         * @param {Tree} tree The owner tree
24893         * @param {Node} this This node
24894         * @param {Node} node The removed node
24895         */
24896        "remove" : true,
24897        /**
24898         * @event move
24899         * Fires when this node is moved to a new location in the tree
24900         * @param {Tree} tree The owner tree
24901         * @param {Node} this This node
24902         * @param {Node} oldParent The old parent of this node
24903         * @param {Node} newParent The new parent of this node
24904         * @param {Number} index The index it was moved to
24905         */
24906        "move" : true,
24907        /**
24908         * @event insert
24909         * Fires when a new child node is inserted.
24910         * @param {Tree} tree The owner tree
24911         * @param {Node} this This node
24912         * @param {Node} node The child node inserted
24913         * @param {Node} refNode The child node the node was inserted before
24914         */
24915        "insert" : true,
24916        /**
24917         * @event beforeappend
24918         * Fires before a new child is appended, return false to cancel the append.
24919         * @param {Tree} tree The owner tree
24920         * @param {Node} this This node
24921         * @param {Node} node The child node to be appended
24922         */
24923        "beforeappend" : true,
24924        /**
24925         * @event beforeremove
24926         * Fires before a child is removed, return false to cancel the remove.
24927         * @param {Tree} tree The owner tree
24928         * @param {Node} this This node
24929         * @param {Node} node The child node to be removed
24930         */
24931        "beforeremove" : true,
24932        /**
24933         * @event beforemove
24934         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24935         * @param {Tree} tree The owner tree
24936         * @param {Node} this This node
24937         * @param {Node} oldParent The parent of this node
24938         * @param {Node} newParent The new parent this node is moving to
24939         * @param {Number} index The index it is being moved to
24940         */
24941        "beforemove" : true,
24942        /**
24943         * @event beforeinsert
24944         * Fires before a new child is inserted, return false to cancel the insert.
24945         * @param {Tree} tree The owner tree
24946         * @param {Node} this This node
24947         * @param {Node} node The child node to be inserted
24948         * @param {Node} refNode The child node the node is being inserted before
24949         */
24950        "beforeinsert" : true
24951    });
24952     this.listeners = this.attributes.listeners;
24953     Roo.data.Node.superclass.constructor.call(this);
24954 };
24955
24956 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24957     fireEvent : function(evtName){
24958         // first do standard event for this node
24959         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24960             return false;
24961         }
24962         // then bubble it up to the tree if the event wasn't cancelled
24963         var ot = this.getOwnerTree();
24964         if(ot){
24965             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24966                 return false;
24967             }
24968         }
24969         return true;
24970     },
24971
24972     /**
24973      * Returns true if this node is a leaf
24974      * @return {Boolean}
24975      */
24976     isLeaf : function(){
24977         return this.leaf === true;
24978     },
24979
24980     // private
24981     setFirstChild : function(node){
24982         this.firstChild = node;
24983     },
24984
24985     //private
24986     setLastChild : function(node){
24987         this.lastChild = node;
24988     },
24989
24990
24991     /**
24992      * Returns true if this node is the last child of its parent
24993      * @return {Boolean}
24994      */
24995     isLast : function(){
24996        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24997     },
24998
24999     /**
25000      * Returns true if this node is the first child of its parent
25001      * @return {Boolean}
25002      */
25003     isFirst : function(){
25004        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25005     },
25006
25007     hasChildNodes : function(){
25008         return !this.isLeaf() && this.childNodes.length > 0;
25009     },
25010
25011     /**
25012      * Insert node(s) as the last child node of this node.
25013      * @param {Node/Array} node The node or Array of nodes to append
25014      * @return {Node} The appended node if single append, or null if an array was passed
25015      */
25016     appendChild : function(node){
25017         var multi = false;
25018         if(node instanceof Array){
25019             multi = node;
25020         }else if(arguments.length > 1){
25021             multi = arguments;
25022         }
25023         
25024         // if passed an array or multiple args do them one by one
25025         if(multi){
25026             for(var i = 0, len = multi.length; i < len; i++) {
25027                 this.appendChild(multi[i]);
25028             }
25029         }else{
25030             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25031                 return false;
25032             }
25033             var index = this.childNodes.length;
25034             var oldParent = node.parentNode;
25035             // it's a move, make sure we move it cleanly
25036             if(oldParent){
25037                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25038                     return false;
25039                 }
25040                 oldParent.removeChild(node);
25041             }
25042             
25043             index = this.childNodes.length;
25044             if(index == 0){
25045                 this.setFirstChild(node);
25046             }
25047             this.childNodes.push(node);
25048             node.parentNode = this;
25049             var ps = this.childNodes[index-1];
25050             if(ps){
25051                 node.previousSibling = ps;
25052                 ps.nextSibling = node;
25053             }else{
25054                 node.previousSibling = null;
25055             }
25056             node.nextSibling = null;
25057             this.setLastChild(node);
25058             node.setOwnerTree(this.getOwnerTree());
25059             this.fireEvent("append", this.ownerTree, this, node, index);
25060             if(this.ownerTree) {
25061                 this.ownerTree.fireEvent("appendnode", this, node, index);
25062             }
25063             if(oldParent){
25064                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25065             }
25066             return node;
25067         }
25068     },
25069
25070     /**
25071      * Removes a child node from this node.
25072      * @param {Node} node The node to remove
25073      * @return {Node} The removed node
25074      */
25075     removeChild : function(node){
25076         var index = this.childNodes.indexOf(node);
25077         if(index == -1){
25078             return false;
25079         }
25080         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25081             return false;
25082         }
25083
25084         // remove it from childNodes collection
25085         this.childNodes.splice(index, 1);
25086
25087         // update siblings
25088         if(node.previousSibling){
25089             node.previousSibling.nextSibling = node.nextSibling;
25090         }
25091         if(node.nextSibling){
25092             node.nextSibling.previousSibling = node.previousSibling;
25093         }
25094
25095         // update child refs
25096         if(this.firstChild == node){
25097             this.setFirstChild(node.nextSibling);
25098         }
25099         if(this.lastChild == node){
25100             this.setLastChild(node.previousSibling);
25101         }
25102
25103         node.setOwnerTree(null);
25104         // clear any references from the node
25105         node.parentNode = null;
25106         node.previousSibling = null;
25107         node.nextSibling = null;
25108         this.fireEvent("remove", this.ownerTree, this, node);
25109         return node;
25110     },
25111
25112     /**
25113      * Inserts the first node before the second node in this nodes childNodes collection.
25114      * @param {Node} node The node to insert
25115      * @param {Node} refNode The node to insert before (if null the node is appended)
25116      * @return {Node} The inserted node
25117      */
25118     insertBefore : function(node, refNode){
25119         if(!refNode){ // like standard Dom, refNode can be null for append
25120             return this.appendChild(node);
25121         }
25122         // nothing to do
25123         if(node == refNode){
25124             return false;
25125         }
25126
25127         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25128             return false;
25129         }
25130         var index = this.childNodes.indexOf(refNode);
25131         var oldParent = node.parentNode;
25132         var refIndex = index;
25133
25134         // when moving internally, indexes will change after remove
25135         if(oldParent == this && this.childNodes.indexOf(node) < index){
25136             refIndex--;
25137         }
25138
25139         // it's a move, make sure we move it cleanly
25140         if(oldParent){
25141             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25142                 return false;
25143             }
25144             oldParent.removeChild(node);
25145         }
25146         if(refIndex == 0){
25147             this.setFirstChild(node);
25148         }
25149         this.childNodes.splice(refIndex, 0, node);
25150         node.parentNode = this;
25151         var ps = this.childNodes[refIndex-1];
25152         if(ps){
25153             node.previousSibling = ps;
25154             ps.nextSibling = node;
25155         }else{
25156             node.previousSibling = null;
25157         }
25158         node.nextSibling = refNode;
25159         refNode.previousSibling = node;
25160         node.setOwnerTree(this.getOwnerTree());
25161         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25162         if(oldParent){
25163             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25164         }
25165         return node;
25166     },
25167
25168     /**
25169      * Returns the child node at the specified index.
25170      * @param {Number} index
25171      * @return {Node}
25172      */
25173     item : function(index){
25174         return this.childNodes[index];
25175     },
25176
25177     /**
25178      * Replaces one child node in this node with another.
25179      * @param {Node} newChild The replacement node
25180      * @param {Node} oldChild The node to replace
25181      * @return {Node} The replaced node
25182      */
25183     replaceChild : function(newChild, oldChild){
25184         this.insertBefore(newChild, oldChild);
25185         this.removeChild(oldChild);
25186         return oldChild;
25187     },
25188
25189     /**
25190      * Returns the index of a child node
25191      * @param {Node} node
25192      * @return {Number} The index of the node or -1 if it was not found
25193      */
25194     indexOf : function(child){
25195         return this.childNodes.indexOf(child);
25196     },
25197
25198     /**
25199      * Returns the tree this node is in.
25200      * @return {Tree}
25201      */
25202     getOwnerTree : function(){
25203         // if it doesn't have one, look for one
25204         if(!this.ownerTree){
25205             var p = this;
25206             while(p){
25207                 if(p.ownerTree){
25208                     this.ownerTree = p.ownerTree;
25209                     break;
25210                 }
25211                 p = p.parentNode;
25212             }
25213         }
25214         return this.ownerTree;
25215     },
25216
25217     /**
25218      * Returns depth of this node (the root node has a depth of 0)
25219      * @return {Number}
25220      */
25221     getDepth : function(){
25222         var depth = 0;
25223         var p = this;
25224         while(p.parentNode){
25225             ++depth;
25226             p = p.parentNode;
25227         }
25228         return depth;
25229     },
25230
25231     // private
25232     setOwnerTree : function(tree){
25233         // if it's move, we need to update everyone
25234         if(tree != this.ownerTree){
25235             if(this.ownerTree){
25236                 this.ownerTree.unregisterNode(this);
25237             }
25238             this.ownerTree = tree;
25239             var cs = this.childNodes;
25240             for(var i = 0, len = cs.length; i < len; i++) {
25241                 cs[i].setOwnerTree(tree);
25242             }
25243             if(tree){
25244                 tree.registerNode(this);
25245             }
25246         }
25247     },
25248
25249     /**
25250      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25251      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25252      * @return {String} The path
25253      */
25254     getPath : function(attr){
25255         attr = attr || "id";
25256         var p = this.parentNode;
25257         var b = [this.attributes[attr]];
25258         while(p){
25259             b.unshift(p.attributes[attr]);
25260             p = p.parentNode;
25261         }
25262         var sep = this.getOwnerTree().pathSeparator;
25263         return sep + b.join(sep);
25264     },
25265
25266     /**
25267      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25268      * function call will be the scope provided or the current node. The arguments to the function
25269      * will be the args provided or the current node. If the function returns false at any point,
25270      * the bubble is stopped.
25271      * @param {Function} fn The function to call
25272      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25273      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25274      */
25275     bubble : function(fn, scope, args){
25276         var p = this;
25277         while(p){
25278             if(fn.call(scope || p, args || p) === false){
25279                 break;
25280             }
25281             p = p.parentNode;
25282         }
25283     },
25284
25285     /**
25286      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25287      * function call will be the scope provided or the current node. The arguments to the function
25288      * will be the args provided or the current node. If the function returns false at any point,
25289      * the cascade is stopped on that branch.
25290      * @param {Function} fn The function to call
25291      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25292      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25293      */
25294     cascade : function(fn, scope, args){
25295         if(fn.call(scope || this, args || this) !== false){
25296             var cs = this.childNodes;
25297             for(var i = 0, len = cs.length; i < len; i++) {
25298                 cs[i].cascade(fn, scope, args);
25299             }
25300         }
25301     },
25302
25303     /**
25304      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25305      * function call will be the scope provided or the current node. The arguments to the function
25306      * will be the args provided or the current node. If the function returns false at any point,
25307      * the iteration stops.
25308      * @param {Function} fn The function to call
25309      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25310      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25311      */
25312     eachChild : function(fn, scope, args){
25313         var cs = this.childNodes;
25314         for(var i = 0, len = cs.length; i < len; i++) {
25315                 if(fn.call(scope || this, args || cs[i]) === false){
25316                     break;
25317                 }
25318         }
25319     },
25320
25321     /**
25322      * Finds the first child that has the attribute with the specified value.
25323      * @param {String} attribute The attribute name
25324      * @param {Mixed} value The value to search for
25325      * @return {Node} The found child or null if none was found
25326      */
25327     findChild : function(attribute, value){
25328         var cs = this.childNodes;
25329         for(var i = 0, len = cs.length; i < len; i++) {
25330                 if(cs[i].attributes[attribute] == value){
25331                     return cs[i];
25332                 }
25333         }
25334         return null;
25335     },
25336
25337     /**
25338      * Finds the first child by a custom function. The child matches if the function passed
25339      * returns true.
25340      * @param {Function} fn
25341      * @param {Object} scope (optional)
25342      * @return {Node} The found child or null if none was found
25343      */
25344     findChildBy : function(fn, scope){
25345         var cs = this.childNodes;
25346         for(var i = 0, len = cs.length; i < len; i++) {
25347                 if(fn.call(scope||cs[i], cs[i]) === true){
25348                     return cs[i];
25349                 }
25350         }
25351         return null;
25352     },
25353
25354     /**
25355      * Sorts this nodes children using the supplied sort function
25356      * @param {Function} fn
25357      * @param {Object} scope (optional)
25358      */
25359     sort : function(fn, scope){
25360         var cs = this.childNodes;
25361         var len = cs.length;
25362         if(len > 0){
25363             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25364             cs.sort(sortFn);
25365             for(var i = 0; i < len; i++){
25366                 var n = cs[i];
25367                 n.previousSibling = cs[i-1];
25368                 n.nextSibling = cs[i+1];
25369                 if(i == 0){
25370                     this.setFirstChild(n);
25371                 }
25372                 if(i == len-1){
25373                     this.setLastChild(n);
25374                 }
25375             }
25376         }
25377     },
25378
25379     /**
25380      * Returns true if this node is an ancestor (at any point) of the passed node.
25381      * @param {Node} node
25382      * @return {Boolean}
25383      */
25384     contains : function(node){
25385         return node.isAncestor(this);
25386     },
25387
25388     /**
25389      * Returns true if the passed node is an ancestor (at any point) of this node.
25390      * @param {Node} node
25391      * @return {Boolean}
25392      */
25393     isAncestor : function(node){
25394         var p = this.parentNode;
25395         while(p){
25396             if(p == node){
25397                 return true;
25398             }
25399             p = p.parentNode;
25400         }
25401         return false;
25402     },
25403
25404     toString : function(){
25405         return "[Node"+(this.id?" "+this.id:"")+"]";
25406     }
25407 });/*
25408  * Based on:
25409  * Ext JS Library 1.1.1
25410  * Copyright(c) 2006-2007, Ext JS, LLC.
25411  *
25412  * Originally Released Under LGPL - original licence link has changed is not relivant.
25413  *
25414  * Fork - LGPL
25415  * <script type="text/javascript">
25416  */
25417  (function(){ 
25418 /**
25419  * @class Roo.Layer
25420  * @extends Roo.Element
25421  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25422  * automatic maintaining of shadow/shim positions.
25423  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25424  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25425  * you can pass a string with a CSS class name. False turns off the shadow.
25426  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25427  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25428  * @cfg {String} cls CSS class to add to the element
25429  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25430  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25431  * @constructor
25432  * @param {Object} config An object with config options.
25433  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25434  */
25435
25436 Roo.Layer = function(config, existingEl){
25437     config = config || {};
25438     var dh = Roo.DomHelper;
25439     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25440     if(existingEl){
25441         this.dom = Roo.getDom(existingEl);
25442     }
25443     if(!this.dom){
25444         var o = config.dh || {tag: "div", cls: "x-layer"};
25445         this.dom = dh.append(pel, o);
25446     }
25447     if(config.cls){
25448         this.addClass(config.cls);
25449     }
25450     this.constrain = config.constrain !== false;
25451     this.visibilityMode = Roo.Element.VISIBILITY;
25452     if(config.id){
25453         this.id = this.dom.id = config.id;
25454     }else{
25455         this.id = Roo.id(this.dom);
25456     }
25457     this.zindex = config.zindex || this.getZIndex();
25458     this.position("absolute", this.zindex);
25459     if(config.shadow){
25460         this.shadowOffset = config.shadowOffset || 4;
25461         this.shadow = new Roo.Shadow({
25462             offset : this.shadowOffset,
25463             mode : config.shadow
25464         });
25465     }else{
25466         this.shadowOffset = 0;
25467     }
25468     this.useShim = config.shim !== false && Roo.useShims;
25469     this.useDisplay = config.useDisplay;
25470     this.hide();
25471 };
25472
25473 var supr = Roo.Element.prototype;
25474
25475 // shims are shared among layer to keep from having 100 iframes
25476 var shims = [];
25477
25478 Roo.extend(Roo.Layer, Roo.Element, {
25479
25480     getZIndex : function(){
25481         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25482     },
25483
25484     getShim : function(){
25485         if(!this.useShim){
25486             return null;
25487         }
25488         if(this.shim){
25489             return this.shim;
25490         }
25491         var shim = shims.shift();
25492         if(!shim){
25493             shim = this.createShim();
25494             shim.enableDisplayMode('block');
25495             shim.dom.style.display = 'none';
25496             shim.dom.style.visibility = 'visible';
25497         }
25498         var pn = this.dom.parentNode;
25499         if(shim.dom.parentNode != pn){
25500             pn.insertBefore(shim.dom, this.dom);
25501         }
25502         shim.setStyle('z-index', this.getZIndex()-2);
25503         this.shim = shim;
25504         return shim;
25505     },
25506
25507     hideShim : function(){
25508         if(this.shim){
25509             this.shim.setDisplayed(false);
25510             shims.push(this.shim);
25511             delete this.shim;
25512         }
25513     },
25514
25515     disableShadow : function(){
25516         if(this.shadow){
25517             this.shadowDisabled = true;
25518             this.shadow.hide();
25519             this.lastShadowOffset = this.shadowOffset;
25520             this.shadowOffset = 0;
25521         }
25522     },
25523
25524     enableShadow : function(show){
25525         if(this.shadow){
25526             this.shadowDisabled = false;
25527             this.shadowOffset = this.lastShadowOffset;
25528             delete this.lastShadowOffset;
25529             if(show){
25530                 this.sync(true);
25531             }
25532         }
25533     },
25534
25535     // private
25536     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25537     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25538     sync : function(doShow){
25539         var sw = this.shadow;
25540         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25541             var sh = this.getShim();
25542
25543             var w = this.getWidth(),
25544                 h = this.getHeight();
25545
25546             var l = this.getLeft(true),
25547                 t = this.getTop(true);
25548
25549             if(sw && !this.shadowDisabled){
25550                 if(doShow && !sw.isVisible()){
25551                     sw.show(this);
25552                 }else{
25553                     sw.realign(l, t, w, h);
25554                 }
25555                 if(sh){
25556                     if(doShow){
25557                        sh.show();
25558                     }
25559                     // fit the shim behind the shadow, so it is shimmed too
25560                     var a = sw.adjusts, s = sh.dom.style;
25561                     s.left = (Math.min(l, l+a.l))+"px";
25562                     s.top = (Math.min(t, t+a.t))+"px";
25563                     s.width = (w+a.w)+"px";
25564                     s.height = (h+a.h)+"px";
25565                 }
25566             }else if(sh){
25567                 if(doShow){
25568                    sh.show();
25569                 }
25570                 sh.setSize(w, h);
25571                 sh.setLeftTop(l, t);
25572             }
25573             
25574         }
25575     },
25576
25577     // private
25578     destroy : function(){
25579         this.hideShim();
25580         if(this.shadow){
25581             this.shadow.hide();
25582         }
25583         this.removeAllListeners();
25584         var pn = this.dom.parentNode;
25585         if(pn){
25586             pn.removeChild(this.dom);
25587         }
25588         Roo.Element.uncache(this.id);
25589     },
25590
25591     remove : function(){
25592         this.destroy();
25593     },
25594
25595     // private
25596     beginUpdate : function(){
25597         this.updating = true;
25598     },
25599
25600     // private
25601     endUpdate : function(){
25602         this.updating = false;
25603         this.sync(true);
25604     },
25605
25606     // private
25607     hideUnders : function(negOffset){
25608         if(this.shadow){
25609             this.shadow.hide();
25610         }
25611         this.hideShim();
25612     },
25613
25614     // private
25615     constrainXY : function(){
25616         if(this.constrain){
25617             var vw = Roo.lib.Dom.getViewWidth(),
25618                 vh = Roo.lib.Dom.getViewHeight();
25619             var s = Roo.get(document).getScroll();
25620
25621             var xy = this.getXY();
25622             var x = xy[0], y = xy[1];   
25623             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25624             // only move it if it needs it
25625             var moved = false;
25626             // first validate right/bottom
25627             if((x + w) > vw+s.left){
25628                 x = vw - w - this.shadowOffset;
25629                 moved = true;
25630             }
25631             if((y + h) > vh+s.top){
25632                 y = vh - h - this.shadowOffset;
25633                 moved = true;
25634             }
25635             // then make sure top/left isn't negative
25636             if(x < s.left){
25637                 x = s.left;
25638                 moved = true;
25639             }
25640             if(y < s.top){
25641                 y = s.top;
25642                 moved = true;
25643             }
25644             if(moved){
25645                 if(this.avoidY){
25646                     var ay = this.avoidY;
25647                     if(y <= ay && (y+h) >= ay){
25648                         y = ay-h-5;   
25649                     }
25650                 }
25651                 xy = [x, y];
25652                 this.storeXY(xy);
25653                 supr.setXY.call(this, xy);
25654                 this.sync();
25655             }
25656         }
25657     },
25658
25659     isVisible : function(){
25660         return this.visible;    
25661     },
25662
25663     // private
25664     showAction : function(){
25665         this.visible = true; // track visibility to prevent getStyle calls
25666         if(this.useDisplay === true){
25667             this.setDisplayed("");
25668         }else if(this.lastXY){
25669             supr.setXY.call(this, this.lastXY);
25670         }else if(this.lastLT){
25671             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25672         }
25673     },
25674
25675     // private
25676     hideAction : function(){
25677         this.visible = false;
25678         if(this.useDisplay === true){
25679             this.setDisplayed(false);
25680         }else{
25681             this.setLeftTop(-10000,-10000);
25682         }
25683     },
25684
25685     // overridden Element method
25686     setVisible : function(v, a, d, c, e){
25687         if(v){
25688             this.showAction();
25689         }
25690         if(a && v){
25691             var cb = function(){
25692                 this.sync(true);
25693                 if(c){
25694                     c();
25695                 }
25696             }.createDelegate(this);
25697             supr.setVisible.call(this, true, true, d, cb, e);
25698         }else{
25699             if(!v){
25700                 this.hideUnders(true);
25701             }
25702             var cb = c;
25703             if(a){
25704                 cb = function(){
25705                     this.hideAction();
25706                     if(c){
25707                         c();
25708                     }
25709                 }.createDelegate(this);
25710             }
25711             supr.setVisible.call(this, v, a, d, cb, e);
25712             if(v){
25713                 this.sync(true);
25714             }else if(!a){
25715                 this.hideAction();
25716             }
25717         }
25718     },
25719
25720     storeXY : function(xy){
25721         delete this.lastLT;
25722         this.lastXY = xy;
25723     },
25724
25725     storeLeftTop : function(left, top){
25726         delete this.lastXY;
25727         this.lastLT = [left, top];
25728     },
25729
25730     // private
25731     beforeFx : function(){
25732         this.beforeAction();
25733         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25734     },
25735
25736     // private
25737     afterFx : function(){
25738         Roo.Layer.superclass.afterFx.apply(this, arguments);
25739         this.sync(this.isVisible());
25740     },
25741
25742     // private
25743     beforeAction : function(){
25744         if(!this.updating && this.shadow){
25745             this.shadow.hide();
25746         }
25747     },
25748
25749     // overridden Element method
25750     setLeft : function(left){
25751         this.storeLeftTop(left, this.getTop(true));
25752         supr.setLeft.apply(this, arguments);
25753         this.sync();
25754     },
25755
25756     setTop : function(top){
25757         this.storeLeftTop(this.getLeft(true), top);
25758         supr.setTop.apply(this, arguments);
25759         this.sync();
25760     },
25761
25762     setLeftTop : function(left, top){
25763         this.storeLeftTop(left, top);
25764         supr.setLeftTop.apply(this, arguments);
25765         this.sync();
25766     },
25767
25768     setXY : function(xy, a, d, c, e){
25769         this.fixDisplay();
25770         this.beforeAction();
25771         this.storeXY(xy);
25772         var cb = this.createCB(c);
25773         supr.setXY.call(this, xy, a, d, cb, e);
25774         if(!a){
25775             cb();
25776         }
25777     },
25778
25779     // private
25780     createCB : function(c){
25781         var el = this;
25782         return function(){
25783             el.constrainXY();
25784             el.sync(true);
25785             if(c){
25786                 c();
25787             }
25788         };
25789     },
25790
25791     // overridden Element method
25792     setX : function(x, a, d, c, e){
25793         this.setXY([x, this.getY()], a, d, c, e);
25794     },
25795
25796     // overridden Element method
25797     setY : function(y, a, d, c, e){
25798         this.setXY([this.getX(), y], a, d, c, e);
25799     },
25800
25801     // overridden Element method
25802     setSize : function(w, h, a, d, c, e){
25803         this.beforeAction();
25804         var cb = this.createCB(c);
25805         supr.setSize.call(this, w, h, a, d, cb, e);
25806         if(!a){
25807             cb();
25808         }
25809     },
25810
25811     // overridden Element method
25812     setWidth : function(w, a, d, c, e){
25813         this.beforeAction();
25814         var cb = this.createCB(c);
25815         supr.setWidth.call(this, w, a, d, cb, e);
25816         if(!a){
25817             cb();
25818         }
25819     },
25820
25821     // overridden Element method
25822     setHeight : function(h, a, d, c, e){
25823         this.beforeAction();
25824         var cb = this.createCB(c);
25825         supr.setHeight.call(this, h, a, d, cb, e);
25826         if(!a){
25827             cb();
25828         }
25829     },
25830
25831     // overridden Element method
25832     setBounds : function(x, y, w, h, a, d, c, e){
25833         this.beforeAction();
25834         var cb = this.createCB(c);
25835         if(!a){
25836             this.storeXY([x, y]);
25837             supr.setXY.call(this, [x, y]);
25838             supr.setSize.call(this, w, h, a, d, cb, e);
25839             cb();
25840         }else{
25841             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25842         }
25843         return this;
25844     },
25845     
25846     /**
25847      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25848      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25849      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25850      * @param {Number} zindex The new z-index to set
25851      * @return {this} The Layer
25852      */
25853     setZIndex : function(zindex){
25854         this.zindex = zindex;
25855         this.setStyle("z-index", zindex + 2);
25856         if(this.shadow){
25857             this.shadow.setZIndex(zindex + 1);
25858         }
25859         if(this.shim){
25860             this.shim.setStyle("z-index", zindex);
25861         }
25862     }
25863 });
25864 })();/*
25865  * Based on:
25866  * Ext JS Library 1.1.1
25867  * Copyright(c) 2006-2007, Ext JS, LLC.
25868  *
25869  * Originally Released Under LGPL - original licence link has changed is not relivant.
25870  *
25871  * Fork - LGPL
25872  * <script type="text/javascript">
25873  */
25874
25875
25876 /**
25877  * @class Roo.Shadow
25878  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25879  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25880  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25881  * @constructor
25882  * Create a new Shadow
25883  * @param {Object} config The config object
25884  */
25885 Roo.Shadow = function(config){
25886     Roo.apply(this, config);
25887     if(typeof this.mode != "string"){
25888         this.mode = this.defaultMode;
25889     }
25890     var o = this.offset, a = {h: 0};
25891     var rad = Math.floor(this.offset/2);
25892     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25893         case "drop":
25894             a.w = 0;
25895             a.l = a.t = o;
25896             a.t -= 1;
25897             if(Roo.isIE){
25898                 a.l -= this.offset + rad;
25899                 a.t -= this.offset + rad;
25900                 a.w -= rad;
25901                 a.h -= rad;
25902                 a.t += 1;
25903             }
25904         break;
25905         case "sides":
25906             a.w = (o*2);
25907             a.l = -o;
25908             a.t = o-1;
25909             if(Roo.isIE){
25910                 a.l -= (this.offset - rad);
25911                 a.t -= this.offset + rad;
25912                 a.l += 1;
25913                 a.w -= (this.offset - rad)*2;
25914                 a.w -= rad + 1;
25915                 a.h -= 1;
25916             }
25917         break;
25918         case "frame":
25919             a.w = a.h = (o*2);
25920             a.l = a.t = -o;
25921             a.t += 1;
25922             a.h -= 2;
25923             if(Roo.isIE){
25924                 a.l -= (this.offset - rad);
25925                 a.t -= (this.offset - rad);
25926                 a.l += 1;
25927                 a.w -= (this.offset + rad + 1);
25928                 a.h -= (this.offset + rad);
25929                 a.h += 1;
25930             }
25931         break;
25932     };
25933
25934     this.adjusts = a;
25935 };
25936
25937 Roo.Shadow.prototype = {
25938     /**
25939      * @cfg {String} mode
25940      * The shadow display mode.  Supports the following options:<br />
25941      * sides: Shadow displays on both sides and bottom only<br />
25942      * frame: Shadow displays equally on all four sides<br />
25943      * drop: Traditional bottom-right drop shadow (default)
25944      */
25945     /**
25946      * @cfg {String} offset
25947      * The number of pixels to offset the shadow from the element (defaults to 4)
25948      */
25949     offset: 4,
25950
25951     // private
25952     defaultMode: "drop",
25953
25954     /**
25955      * Displays the shadow under the target element
25956      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25957      */
25958     show : function(target){
25959         target = Roo.get(target);
25960         if(!this.el){
25961             this.el = Roo.Shadow.Pool.pull();
25962             if(this.el.dom.nextSibling != target.dom){
25963                 this.el.insertBefore(target);
25964             }
25965         }
25966         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25967         if(Roo.isIE){
25968             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25969         }
25970         this.realign(
25971             target.getLeft(true),
25972             target.getTop(true),
25973             target.getWidth(),
25974             target.getHeight()
25975         );
25976         this.el.dom.style.display = "block";
25977     },
25978
25979     /**
25980      * Returns true if the shadow is visible, else false
25981      */
25982     isVisible : function(){
25983         return this.el ? true : false;  
25984     },
25985
25986     /**
25987      * Direct alignment when values are already available. Show must be called at least once before
25988      * calling this method to ensure it is initialized.
25989      * @param {Number} left The target element left position
25990      * @param {Number} top The target element top position
25991      * @param {Number} width The target element width
25992      * @param {Number} height The target element height
25993      */
25994     realign : function(l, t, w, h){
25995         if(!this.el){
25996             return;
25997         }
25998         var a = this.adjusts, d = this.el.dom, s = d.style;
25999         var iea = 0;
26000         s.left = (l+a.l)+"px";
26001         s.top = (t+a.t)+"px";
26002         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26003  
26004         if(s.width != sws || s.height != shs){
26005             s.width = sws;
26006             s.height = shs;
26007             if(!Roo.isIE){
26008                 var cn = d.childNodes;
26009                 var sww = Math.max(0, (sw-12))+"px";
26010                 cn[0].childNodes[1].style.width = sww;
26011                 cn[1].childNodes[1].style.width = sww;
26012                 cn[2].childNodes[1].style.width = sww;
26013                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26014             }
26015         }
26016     },
26017
26018     /**
26019      * Hides this shadow
26020      */
26021     hide : function(){
26022         if(this.el){
26023             this.el.dom.style.display = "none";
26024             Roo.Shadow.Pool.push(this.el);
26025             delete this.el;
26026         }
26027     },
26028
26029     /**
26030      * Adjust the z-index of this shadow
26031      * @param {Number} zindex The new z-index
26032      */
26033     setZIndex : function(z){
26034         this.zIndex = z;
26035         if(this.el){
26036             this.el.setStyle("z-index", z);
26037         }
26038     }
26039 };
26040
26041 // Private utility class that manages the internal Shadow cache
26042 Roo.Shadow.Pool = function(){
26043     var p = [];
26044     var markup = Roo.isIE ?
26045                  '<div class="x-ie-shadow"></div>' :
26046                  '<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>';
26047     return {
26048         pull : function(){
26049             var sh = p.shift();
26050             if(!sh){
26051                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26052                 sh.autoBoxAdjust = false;
26053             }
26054             return sh;
26055         },
26056
26057         push : function(sh){
26058             p.push(sh);
26059         }
26060     };
26061 }();/*
26062  * Based on:
26063  * Ext JS Library 1.1.1
26064  * Copyright(c) 2006-2007, Ext JS, LLC.
26065  *
26066  * Originally Released Under LGPL - original licence link has changed is not relivant.
26067  *
26068  * Fork - LGPL
26069  * <script type="text/javascript">
26070  */
26071
26072
26073 /**
26074  * @class Roo.SplitBar
26075  * @extends Roo.util.Observable
26076  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26077  * <br><br>
26078  * Usage:
26079  * <pre><code>
26080 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26081                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26082 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26083 split.minSize = 100;
26084 split.maxSize = 600;
26085 split.animate = true;
26086 split.on('moved', splitterMoved);
26087 </code></pre>
26088  * @constructor
26089  * Create a new SplitBar
26090  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26091  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26092  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26093  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26094                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26095                         position of the SplitBar).
26096  */
26097 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26098     
26099     /** @private */
26100     this.el = Roo.get(dragElement, true);
26101     this.el.dom.unselectable = "on";
26102     /** @private */
26103     this.resizingEl = Roo.get(resizingElement, true);
26104
26105     /**
26106      * @private
26107      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26108      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26109      * @type Number
26110      */
26111     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26112     
26113     /**
26114      * The minimum size of the resizing element. (Defaults to 0)
26115      * @type Number
26116      */
26117     this.minSize = 0;
26118     
26119     /**
26120      * The maximum size of the resizing element. (Defaults to 2000)
26121      * @type Number
26122      */
26123     this.maxSize = 2000;
26124     
26125     /**
26126      * Whether to animate the transition to the new size
26127      * @type Boolean
26128      */
26129     this.animate = false;
26130     
26131     /**
26132      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26133      * @type Boolean
26134      */
26135     this.useShim = false;
26136     
26137     /** @private */
26138     this.shim = null;
26139     
26140     if(!existingProxy){
26141         /** @private */
26142         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26143     }else{
26144         this.proxy = Roo.get(existingProxy).dom;
26145     }
26146     /** @private */
26147     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26148     
26149     /** @private */
26150     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26151     
26152     /** @private */
26153     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26154     
26155     /** @private */
26156     this.dragSpecs = {};
26157     
26158     /**
26159      * @private The adapter to use to positon and resize elements
26160      */
26161     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26162     this.adapter.init(this);
26163     
26164     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26165         /** @private */
26166         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26167         this.el.addClass("x-splitbar-h");
26168     }else{
26169         /** @private */
26170         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26171         this.el.addClass("x-splitbar-v");
26172     }
26173     
26174     this.addEvents({
26175         /**
26176          * @event resize
26177          * Fires when the splitter is moved (alias for {@link #event-moved})
26178          * @param {Roo.SplitBar} this
26179          * @param {Number} newSize the new width or height
26180          */
26181         "resize" : true,
26182         /**
26183          * @event moved
26184          * Fires when the splitter is moved
26185          * @param {Roo.SplitBar} this
26186          * @param {Number} newSize the new width or height
26187          */
26188         "moved" : true,
26189         /**
26190          * @event beforeresize
26191          * Fires before the splitter is dragged
26192          * @param {Roo.SplitBar} this
26193          */
26194         "beforeresize" : true,
26195
26196         "beforeapply" : true
26197     });
26198
26199     Roo.util.Observable.call(this);
26200 };
26201
26202 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26203     onStartProxyDrag : function(x, y){
26204         this.fireEvent("beforeresize", this);
26205         if(!this.overlay){
26206             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26207             o.unselectable();
26208             o.enableDisplayMode("block");
26209             // all splitbars share the same overlay
26210             Roo.SplitBar.prototype.overlay = o;
26211         }
26212         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26213         this.overlay.show();
26214         Roo.get(this.proxy).setDisplayed("block");
26215         var size = this.adapter.getElementSize(this);
26216         this.activeMinSize = this.getMinimumSize();;
26217         this.activeMaxSize = this.getMaximumSize();;
26218         var c1 = size - this.activeMinSize;
26219         var c2 = Math.max(this.activeMaxSize - size, 0);
26220         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26221             this.dd.resetConstraints();
26222             this.dd.setXConstraint(
26223                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26224                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26225             );
26226             this.dd.setYConstraint(0, 0);
26227         }else{
26228             this.dd.resetConstraints();
26229             this.dd.setXConstraint(0, 0);
26230             this.dd.setYConstraint(
26231                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26232                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26233             );
26234          }
26235         this.dragSpecs.startSize = size;
26236         this.dragSpecs.startPoint = [x, y];
26237         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26238     },
26239     
26240     /** 
26241      * @private Called after the drag operation by the DDProxy
26242      */
26243     onEndProxyDrag : function(e){
26244         Roo.get(this.proxy).setDisplayed(false);
26245         var endPoint = Roo.lib.Event.getXY(e);
26246         if(this.overlay){
26247             this.overlay.hide();
26248         }
26249         var newSize;
26250         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26251             newSize = this.dragSpecs.startSize + 
26252                 (this.placement == Roo.SplitBar.LEFT ?
26253                     endPoint[0] - this.dragSpecs.startPoint[0] :
26254                     this.dragSpecs.startPoint[0] - endPoint[0]
26255                 );
26256         }else{
26257             newSize = this.dragSpecs.startSize + 
26258                 (this.placement == Roo.SplitBar.TOP ?
26259                     endPoint[1] - this.dragSpecs.startPoint[1] :
26260                     this.dragSpecs.startPoint[1] - endPoint[1]
26261                 );
26262         }
26263         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26264         if(newSize != this.dragSpecs.startSize){
26265             if(this.fireEvent('beforeapply', this, newSize) !== false){
26266                 this.adapter.setElementSize(this, newSize);
26267                 this.fireEvent("moved", this, newSize);
26268                 this.fireEvent("resize", this, newSize);
26269             }
26270         }
26271     },
26272     
26273     /**
26274      * Get the adapter this SplitBar uses
26275      * @return The adapter object
26276      */
26277     getAdapter : function(){
26278         return this.adapter;
26279     },
26280     
26281     /**
26282      * Set the adapter this SplitBar uses
26283      * @param {Object} adapter A SplitBar adapter object
26284      */
26285     setAdapter : function(adapter){
26286         this.adapter = adapter;
26287         this.adapter.init(this);
26288     },
26289     
26290     /**
26291      * Gets the minimum size for the resizing element
26292      * @return {Number} The minimum size
26293      */
26294     getMinimumSize : function(){
26295         return this.minSize;
26296     },
26297     
26298     /**
26299      * Sets the minimum size for the resizing element
26300      * @param {Number} minSize The minimum size
26301      */
26302     setMinimumSize : function(minSize){
26303         this.minSize = minSize;
26304     },
26305     
26306     /**
26307      * Gets the maximum size for the resizing element
26308      * @return {Number} The maximum size
26309      */
26310     getMaximumSize : function(){
26311         return this.maxSize;
26312     },
26313     
26314     /**
26315      * Sets the maximum size for the resizing element
26316      * @param {Number} maxSize The maximum size
26317      */
26318     setMaximumSize : function(maxSize){
26319         this.maxSize = maxSize;
26320     },
26321     
26322     /**
26323      * Sets the initialize size for the resizing element
26324      * @param {Number} size The initial size
26325      */
26326     setCurrentSize : function(size){
26327         var oldAnimate = this.animate;
26328         this.animate = false;
26329         this.adapter.setElementSize(this, size);
26330         this.animate = oldAnimate;
26331     },
26332     
26333     /**
26334      * Destroy this splitbar. 
26335      * @param {Boolean} removeEl True to remove the element
26336      */
26337     destroy : function(removeEl){
26338         if(this.shim){
26339             this.shim.remove();
26340         }
26341         this.dd.unreg();
26342         this.proxy.parentNode.removeChild(this.proxy);
26343         if(removeEl){
26344             this.el.remove();
26345         }
26346     }
26347 });
26348
26349 /**
26350  * @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.
26351  */
26352 Roo.SplitBar.createProxy = function(dir){
26353     var proxy = new Roo.Element(document.createElement("div"));
26354     proxy.unselectable();
26355     var cls = 'x-splitbar-proxy';
26356     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26357     document.body.appendChild(proxy.dom);
26358     return proxy.dom;
26359 };
26360
26361 /** 
26362  * @class Roo.SplitBar.BasicLayoutAdapter
26363  * Default Adapter. It assumes the splitter and resizing element are not positioned
26364  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26365  */
26366 Roo.SplitBar.BasicLayoutAdapter = function(){
26367 };
26368
26369 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26370     // do nothing for now
26371     init : function(s){
26372     
26373     },
26374     /**
26375      * Called before drag operations to get the current size of the resizing element. 
26376      * @param {Roo.SplitBar} s The SplitBar using this adapter
26377      */
26378      getElementSize : function(s){
26379         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26380             return s.resizingEl.getWidth();
26381         }else{
26382             return s.resizingEl.getHeight();
26383         }
26384     },
26385     
26386     /**
26387      * Called after drag operations to set the size of the resizing element.
26388      * @param {Roo.SplitBar} s The SplitBar using this adapter
26389      * @param {Number} newSize The new size to set
26390      * @param {Function} onComplete A function to be invoked when resizing is complete
26391      */
26392     setElementSize : function(s, newSize, onComplete){
26393         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26394             if(!s.animate){
26395                 s.resizingEl.setWidth(newSize);
26396                 if(onComplete){
26397                     onComplete(s, newSize);
26398                 }
26399             }else{
26400                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26401             }
26402         }else{
26403             
26404             if(!s.animate){
26405                 s.resizingEl.setHeight(newSize);
26406                 if(onComplete){
26407                     onComplete(s, newSize);
26408                 }
26409             }else{
26410                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26411             }
26412         }
26413     }
26414 };
26415
26416 /** 
26417  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26418  * @extends Roo.SplitBar.BasicLayoutAdapter
26419  * Adapter that  moves the splitter element to align with the resized sizing element. 
26420  * Used with an absolute positioned SplitBar.
26421  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26422  * document.body, make sure you assign an id to the body element.
26423  */
26424 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26425     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26426     this.container = Roo.get(container);
26427 };
26428
26429 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26430     init : function(s){
26431         this.basic.init(s);
26432     },
26433     
26434     getElementSize : function(s){
26435         return this.basic.getElementSize(s);
26436     },
26437     
26438     setElementSize : function(s, newSize, onComplete){
26439         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26440     },
26441     
26442     moveSplitter : function(s){
26443         var yes = Roo.SplitBar;
26444         switch(s.placement){
26445             case yes.LEFT:
26446                 s.el.setX(s.resizingEl.getRight());
26447                 break;
26448             case yes.RIGHT:
26449                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26450                 break;
26451             case yes.TOP:
26452                 s.el.setY(s.resizingEl.getBottom());
26453                 break;
26454             case yes.BOTTOM:
26455                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26456                 break;
26457         }
26458     }
26459 };
26460
26461 /**
26462  * Orientation constant - Create a vertical SplitBar
26463  * @static
26464  * @type Number
26465  */
26466 Roo.SplitBar.VERTICAL = 1;
26467
26468 /**
26469  * Orientation constant - Create a horizontal SplitBar
26470  * @static
26471  * @type Number
26472  */
26473 Roo.SplitBar.HORIZONTAL = 2;
26474
26475 /**
26476  * Placement constant - The resizing element is to the left of the splitter element
26477  * @static
26478  * @type Number
26479  */
26480 Roo.SplitBar.LEFT = 1;
26481
26482 /**
26483  * Placement constant - The resizing element is to the right of the splitter element
26484  * @static
26485  * @type Number
26486  */
26487 Roo.SplitBar.RIGHT = 2;
26488
26489 /**
26490  * Placement constant - The resizing element is positioned above the splitter element
26491  * @static
26492  * @type Number
26493  */
26494 Roo.SplitBar.TOP = 3;
26495
26496 /**
26497  * Placement constant - The resizing element is positioned under splitter element
26498  * @static
26499  * @type Number
26500  */
26501 Roo.SplitBar.BOTTOM = 4;
26502 /*
26503  * Based on:
26504  * Ext JS Library 1.1.1
26505  * Copyright(c) 2006-2007, Ext JS, LLC.
26506  *
26507  * Originally Released Under LGPL - original licence link has changed is not relivant.
26508  *
26509  * Fork - LGPL
26510  * <script type="text/javascript">
26511  */
26512
26513 /**
26514  * @class Roo.View
26515  * @extends Roo.util.Observable
26516  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26517  * This class also supports single and multi selection modes. <br>
26518  * Create a data model bound view:
26519  <pre><code>
26520  var store = new Roo.data.Store(...);
26521
26522  var view = new Roo.View({
26523     el : "my-element",
26524     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26525  
26526     singleSelect: true,
26527     selectedClass: "ydataview-selected",
26528     store: store
26529  });
26530
26531  // listen for node click?
26532  view.on("click", function(vw, index, node, e){
26533  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26534  });
26535
26536  // load XML data
26537  dataModel.load("foobar.xml");
26538  </code></pre>
26539  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26540  * <br><br>
26541  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26542  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26543  * 
26544  * Note: old style constructor is still suported (container, template, config)
26545  * 
26546  * @constructor
26547  * Create a new View
26548  * @param {Object} config The config object
26549  * 
26550  */
26551 Roo.View = function(config, depreciated_tpl, depreciated_config){
26552     
26553     this.parent = false;
26554     
26555     if (typeof(depreciated_tpl) == 'undefined') {
26556         // new way.. - universal constructor.
26557         Roo.apply(this, config);
26558         this.el  = Roo.get(this.el);
26559     } else {
26560         // old format..
26561         this.el  = Roo.get(config);
26562         this.tpl = depreciated_tpl;
26563         Roo.apply(this, depreciated_config);
26564     }
26565     this.wrapEl  = this.el.wrap().wrap();
26566     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26567     
26568     
26569     if(typeof(this.tpl) == "string"){
26570         this.tpl = new Roo.Template(this.tpl);
26571     } else {
26572         // support xtype ctors..
26573         this.tpl = new Roo.factory(this.tpl, Roo);
26574     }
26575     
26576     
26577     this.tpl.compile();
26578     
26579     /** @private */
26580     this.addEvents({
26581         /**
26582          * @event beforeclick
26583          * Fires before a click is processed. Returns false to cancel the default action.
26584          * @param {Roo.View} this
26585          * @param {Number} index The index of the target node
26586          * @param {HTMLElement} node The target node
26587          * @param {Roo.EventObject} e The raw event object
26588          */
26589             "beforeclick" : true,
26590         /**
26591          * @event click
26592          * Fires when a template node is clicked.
26593          * @param {Roo.View} this
26594          * @param {Number} index The index of the target node
26595          * @param {HTMLElement} node The target node
26596          * @param {Roo.EventObject} e The raw event object
26597          */
26598             "click" : true,
26599         /**
26600          * @event dblclick
26601          * Fires when a template node is double clicked.
26602          * @param {Roo.View} this
26603          * @param {Number} index The index of the target node
26604          * @param {HTMLElement} node The target node
26605          * @param {Roo.EventObject} e The raw event object
26606          */
26607             "dblclick" : true,
26608         /**
26609          * @event contextmenu
26610          * Fires when a template node is right clicked.
26611          * @param {Roo.View} this
26612          * @param {Number} index The index of the target node
26613          * @param {HTMLElement} node The target node
26614          * @param {Roo.EventObject} e The raw event object
26615          */
26616             "contextmenu" : true,
26617         /**
26618          * @event selectionchange
26619          * Fires when the selected nodes change.
26620          * @param {Roo.View} this
26621          * @param {Array} selections Array of the selected nodes
26622          */
26623             "selectionchange" : true,
26624     
26625         /**
26626          * @event beforeselect
26627          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26628          * @param {Roo.View} this
26629          * @param {HTMLElement} node The node to be selected
26630          * @param {Array} selections Array of currently selected nodes
26631          */
26632             "beforeselect" : true,
26633         /**
26634          * @event preparedata
26635          * Fires on every row to render, to allow you to change the data.
26636          * @param {Roo.View} this
26637          * @param {Object} data to be rendered (change this)
26638          */
26639           "preparedata" : true
26640           
26641           
26642         });
26643
26644
26645
26646     this.el.on({
26647         "click": this.onClick,
26648         "dblclick": this.onDblClick,
26649         "contextmenu": this.onContextMenu,
26650         scope:this
26651     });
26652
26653     this.selections = [];
26654     this.nodes = [];
26655     this.cmp = new Roo.CompositeElementLite([]);
26656     if(this.store){
26657         this.store = Roo.factory(this.store, Roo.data);
26658         this.setStore(this.store, true);
26659     }
26660     
26661     if ( this.footer && this.footer.xtype) {
26662            
26663          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26664         
26665         this.footer.dataSource = this.store;
26666         this.footer.container = fctr;
26667         this.footer = Roo.factory(this.footer, Roo);
26668         fctr.insertFirst(this.el);
26669         
26670         // this is a bit insane - as the paging toolbar seems to detach the el..
26671 //        dom.parentNode.parentNode.parentNode
26672          // they get detached?
26673     }
26674     
26675     
26676     Roo.View.superclass.constructor.call(this);
26677     
26678     
26679 };
26680
26681 Roo.extend(Roo.View, Roo.util.Observable, {
26682     
26683      /**
26684      * @cfg {Roo.data.Store} store Data store to load data from.
26685      */
26686     store : false,
26687     
26688     /**
26689      * @cfg {String|Roo.Element} el The container element.
26690      */
26691     el : '',
26692     
26693     /**
26694      * @cfg {String|Roo.Template} tpl The template used by this View 
26695      */
26696     tpl : false,
26697     /**
26698      * @cfg {String} dataName the named area of the template to use as the data area
26699      *                          Works with domtemplates roo-name="name"
26700      */
26701     dataName: false,
26702     /**
26703      * @cfg {String} selectedClass The css class to add to selected nodes
26704      */
26705     selectedClass : "x-view-selected",
26706      /**
26707      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26708      */
26709     emptyText : "",
26710     
26711     /**
26712      * @cfg {String} text to display on mask (default Loading)
26713      */
26714     mask : false,
26715     /**
26716      * @cfg {Boolean} multiSelect Allow multiple selection
26717      */
26718     multiSelect : false,
26719     /**
26720      * @cfg {Boolean} singleSelect Allow single selection
26721      */
26722     singleSelect:  false,
26723     
26724     /**
26725      * @cfg {Boolean} toggleSelect - selecting 
26726      */
26727     toggleSelect : false,
26728     
26729     /**
26730      * @cfg {Boolean} tickable - selecting 
26731      */
26732     tickable : false,
26733     
26734     /**
26735      * Returns the element this view is bound to.
26736      * @return {Roo.Element}
26737      */
26738     getEl : function(){
26739         return this.wrapEl;
26740     },
26741     
26742     
26743
26744     /**
26745      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26746      */
26747     refresh : function(){
26748         //Roo.log('refresh');
26749         var t = this.tpl;
26750         
26751         // if we are using something like 'domtemplate', then
26752         // the what gets used is:
26753         // t.applySubtemplate(NAME, data, wrapping data..)
26754         // the outer template then get' applied with
26755         //     the store 'extra data'
26756         // and the body get's added to the
26757         //      roo-name="data" node?
26758         //      <span class='roo-tpl-{name}'></span> ?????
26759         
26760         
26761         
26762         this.clearSelections();
26763         this.el.update("");
26764         var html = [];
26765         var records = this.store.getRange();
26766         if(records.length < 1) {
26767             
26768             // is this valid??  = should it render a template??
26769             
26770             this.el.update(this.emptyText);
26771             return;
26772         }
26773         var el = this.el;
26774         if (this.dataName) {
26775             this.el.update(t.apply(this.store.meta)); //????
26776             el = this.el.child('.roo-tpl-' + this.dataName);
26777         }
26778         
26779         for(var i = 0, len = records.length; i < len; i++){
26780             var data = this.prepareData(records[i].data, i, records[i]);
26781             this.fireEvent("preparedata", this, data, i, records[i]);
26782             
26783             var d = Roo.apply({}, data);
26784             
26785             if(this.tickable){
26786                 Roo.apply(d, {'roo-id' : Roo.id()});
26787                 
26788                 var _this = this;
26789             
26790                 Roo.each(this.parent.item, function(item){
26791                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26792                         return;
26793                     }
26794                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26795                 });
26796             }
26797             
26798             html[html.length] = Roo.util.Format.trim(
26799                 this.dataName ?
26800                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26801                     t.apply(d)
26802             );
26803         }
26804         
26805         
26806         
26807         el.update(html.join(""));
26808         this.nodes = el.dom.childNodes;
26809         this.updateIndexes(0);
26810     },
26811     
26812
26813     /**
26814      * Function to override to reformat the data that is sent to
26815      * the template for each node.
26816      * DEPRICATED - use the preparedata event handler.
26817      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26818      * a JSON object for an UpdateManager bound view).
26819      */
26820     prepareData : function(data, index, record)
26821     {
26822         this.fireEvent("preparedata", this, data, index, record);
26823         return data;
26824     },
26825
26826     onUpdate : function(ds, record){
26827         // Roo.log('on update');   
26828         this.clearSelections();
26829         var index = this.store.indexOf(record);
26830         var n = this.nodes[index];
26831         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26832         n.parentNode.removeChild(n);
26833         this.updateIndexes(index, index);
26834     },
26835
26836     
26837     
26838 // --------- FIXME     
26839     onAdd : function(ds, records, index)
26840     {
26841         //Roo.log(['on Add', ds, records, index] );        
26842         this.clearSelections();
26843         if(this.nodes.length == 0){
26844             this.refresh();
26845             return;
26846         }
26847         var n = this.nodes[index];
26848         for(var i = 0, len = records.length; i < len; i++){
26849             var d = this.prepareData(records[i].data, i, records[i]);
26850             if(n){
26851                 this.tpl.insertBefore(n, d);
26852             }else{
26853                 
26854                 this.tpl.append(this.el, d);
26855             }
26856         }
26857         this.updateIndexes(index);
26858     },
26859
26860     onRemove : function(ds, record, index){
26861        // Roo.log('onRemove');
26862         this.clearSelections();
26863         var el = this.dataName  ?
26864             this.el.child('.roo-tpl-' + this.dataName) :
26865             this.el; 
26866         
26867         el.dom.removeChild(this.nodes[index]);
26868         this.updateIndexes(index);
26869     },
26870
26871     /**
26872      * Refresh an individual node.
26873      * @param {Number} index
26874      */
26875     refreshNode : function(index){
26876         this.onUpdate(this.store, this.store.getAt(index));
26877     },
26878
26879     updateIndexes : function(startIndex, endIndex){
26880         var ns = this.nodes;
26881         startIndex = startIndex || 0;
26882         endIndex = endIndex || ns.length - 1;
26883         for(var i = startIndex; i <= endIndex; i++){
26884             ns[i].nodeIndex = i;
26885         }
26886     },
26887
26888     /**
26889      * Changes the data store this view uses and refresh the view.
26890      * @param {Store} store
26891      */
26892     setStore : function(store, initial){
26893         if(!initial && this.store){
26894             this.store.un("datachanged", this.refresh);
26895             this.store.un("add", this.onAdd);
26896             this.store.un("remove", this.onRemove);
26897             this.store.un("update", this.onUpdate);
26898             this.store.un("clear", this.refresh);
26899             this.store.un("beforeload", this.onBeforeLoad);
26900             this.store.un("load", this.onLoad);
26901             this.store.un("loadexception", this.onLoad);
26902         }
26903         if(store){
26904           
26905             store.on("datachanged", this.refresh, this);
26906             store.on("add", this.onAdd, this);
26907             store.on("remove", this.onRemove, this);
26908             store.on("update", this.onUpdate, this);
26909             store.on("clear", this.refresh, this);
26910             store.on("beforeload", this.onBeforeLoad, this);
26911             store.on("load", this.onLoad, this);
26912             store.on("loadexception", this.onLoad, this);
26913         }
26914         
26915         if(store){
26916             this.refresh();
26917         }
26918     },
26919     /**
26920      * onbeforeLoad - masks the loading area.
26921      *
26922      */
26923     onBeforeLoad : function(store,opts)
26924     {
26925          //Roo.log('onBeforeLoad');   
26926         if (!opts.add) {
26927             this.el.update("");
26928         }
26929         this.el.mask(this.mask ? this.mask : "Loading" ); 
26930     },
26931     onLoad : function ()
26932     {
26933         this.el.unmask();
26934     },
26935     
26936
26937     /**
26938      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26939      * @param {HTMLElement} node
26940      * @return {HTMLElement} The template node
26941      */
26942     findItemFromChild : function(node){
26943         var el = this.dataName  ?
26944             this.el.child('.roo-tpl-' + this.dataName,true) :
26945             this.el.dom; 
26946         
26947         if(!node || node.parentNode == el){
26948                     return node;
26949             }
26950             var p = node.parentNode;
26951             while(p && p != el){
26952             if(p.parentNode == el){
26953                 return p;
26954             }
26955             p = p.parentNode;
26956         }
26957             return null;
26958     },
26959
26960     /** @ignore */
26961     onClick : function(e){
26962         var item = this.findItemFromChild(e.getTarget());
26963         if(item){
26964             var index = this.indexOf(item);
26965             if(this.onItemClick(item, index, e) !== false){
26966                 this.fireEvent("click", this, index, item, e);
26967             }
26968         }else{
26969             this.clearSelections();
26970         }
26971     },
26972
26973     /** @ignore */
26974     onContextMenu : function(e){
26975         var item = this.findItemFromChild(e.getTarget());
26976         if(item){
26977             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26978         }
26979     },
26980
26981     /** @ignore */
26982     onDblClick : function(e){
26983         var item = this.findItemFromChild(e.getTarget());
26984         if(item){
26985             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26986         }
26987     },
26988
26989     onItemClick : function(item, index, e)
26990     {
26991         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26992             return false;
26993         }
26994         if (this.toggleSelect) {
26995             var m = this.isSelected(item) ? 'unselect' : 'select';
26996             //Roo.log(m);
26997             var _t = this;
26998             _t[m](item, true, false);
26999             return true;
27000         }
27001         if(this.multiSelect || this.singleSelect){
27002             if(this.multiSelect && e.shiftKey && this.lastSelection){
27003                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27004             }else{
27005                 this.select(item, this.multiSelect && e.ctrlKey);
27006                 this.lastSelection = item;
27007             }
27008             
27009             if(!this.tickable){
27010                 e.preventDefault();
27011             }
27012             
27013         }
27014         return true;
27015     },
27016
27017     /**
27018      * Get the number of selected nodes.
27019      * @return {Number}
27020      */
27021     getSelectionCount : function(){
27022         return this.selections.length;
27023     },
27024
27025     /**
27026      * Get the currently selected nodes.
27027      * @return {Array} An array of HTMLElements
27028      */
27029     getSelectedNodes : function(){
27030         return this.selections;
27031     },
27032
27033     /**
27034      * Get the indexes of the selected nodes.
27035      * @return {Array}
27036      */
27037     getSelectedIndexes : function(){
27038         var indexes = [], s = this.selections;
27039         for(var i = 0, len = s.length; i < len; i++){
27040             indexes.push(s[i].nodeIndex);
27041         }
27042         return indexes;
27043     },
27044
27045     /**
27046      * Clear all selections
27047      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27048      */
27049     clearSelections : function(suppressEvent){
27050         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27051             this.cmp.elements = this.selections;
27052             this.cmp.removeClass(this.selectedClass);
27053             this.selections = [];
27054             if(!suppressEvent){
27055                 this.fireEvent("selectionchange", this, this.selections);
27056             }
27057         }
27058     },
27059
27060     /**
27061      * Returns true if the passed node is selected
27062      * @param {HTMLElement/Number} node The node or node index
27063      * @return {Boolean}
27064      */
27065     isSelected : function(node){
27066         var s = this.selections;
27067         if(s.length < 1){
27068             return false;
27069         }
27070         node = this.getNode(node);
27071         return s.indexOf(node) !== -1;
27072     },
27073
27074     /**
27075      * Selects nodes.
27076      * @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
27077      * @param {Boolean} keepExisting (optional) true to keep existing selections
27078      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27079      */
27080     select : function(nodeInfo, keepExisting, suppressEvent){
27081         if(nodeInfo instanceof Array){
27082             if(!keepExisting){
27083                 this.clearSelections(true);
27084             }
27085             for(var i = 0, len = nodeInfo.length; i < len; i++){
27086                 this.select(nodeInfo[i], true, true);
27087             }
27088             return;
27089         } 
27090         var node = this.getNode(nodeInfo);
27091         if(!node || this.isSelected(node)){
27092             return; // already selected.
27093         }
27094         if(!keepExisting){
27095             this.clearSelections(true);
27096         }
27097         
27098         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27099             Roo.fly(node).addClass(this.selectedClass);
27100             this.selections.push(node);
27101             if(!suppressEvent){
27102                 this.fireEvent("selectionchange", this, this.selections);
27103             }
27104         }
27105         
27106         
27107     },
27108       /**
27109      * Unselects nodes.
27110      * @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
27111      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27112      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27113      */
27114     unselect : function(nodeInfo, keepExisting, suppressEvent)
27115     {
27116         if(nodeInfo instanceof Array){
27117             Roo.each(this.selections, function(s) {
27118                 this.unselect(s, nodeInfo);
27119             }, this);
27120             return;
27121         }
27122         var node = this.getNode(nodeInfo);
27123         if(!node || !this.isSelected(node)){
27124             //Roo.log("not selected");
27125             return; // not selected.
27126         }
27127         // fireevent???
27128         var ns = [];
27129         Roo.each(this.selections, function(s) {
27130             if (s == node ) {
27131                 Roo.fly(node).removeClass(this.selectedClass);
27132
27133                 return;
27134             }
27135             ns.push(s);
27136         },this);
27137         
27138         this.selections= ns;
27139         this.fireEvent("selectionchange", this, this.selections);
27140     },
27141
27142     /**
27143      * Gets a template node.
27144      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27145      * @return {HTMLElement} The node or null if it wasn't found
27146      */
27147     getNode : function(nodeInfo){
27148         if(typeof nodeInfo == "string"){
27149             return document.getElementById(nodeInfo);
27150         }else if(typeof nodeInfo == "number"){
27151             return this.nodes[nodeInfo];
27152         }
27153         return nodeInfo;
27154     },
27155
27156     /**
27157      * Gets a range template nodes.
27158      * @param {Number} startIndex
27159      * @param {Number} endIndex
27160      * @return {Array} An array of nodes
27161      */
27162     getNodes : function(start, end){
27163         var ns = this.nodes;
27164         start = start || 0;
27165         end = typeof end == "undefined" ? ns.length - 1 : end;
27166         var nodes = [];
27167         if(start <= end){
27168             for(var i = start; i <= end; i++){
27169                 nodes.push(ns[i]);
27170             }
27171         } else{
27172             for(var i = start; i >= end; i--){
27173                 nodes.push(ns[i]);
27174             }
27175         }
27176         return nodes;
27177     },
27178
27179     /**
27180      * Finds the index of the passed node
27181      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27182      * @return {Number} The index of the node or -1
27183      */
27184     indexOf : function(node){
27185         node = this.getNode(node);
27186         if(typeof node.nodeIndex == "number"){
27187             return node.nodeIndex;
27188         }
27189         var ns = this.nodes;
27190         for(var i = 0, len = ns.length; i < len; i++){
27191             if(ns[i] == node){
27192                 return i;
27193             }
27194         }
27195         return -1;
27196     }
27197 });
27198 /*
27199  * Based on:
27200  * Ext JS Library 1.1.1
27201  * Copyright(c) 2006-2007, Ext JS, LLC.
27202  *
27203  * Originally Released Under LGPL - original licence link has changed is not relivant.
27204  *
27205  * Fork - LGPL
27206  * <script type="text/javascript">
27207  */
27208
27209 /**
27210  * @class Roo.JsonView
27211  * @extends Roo.View
27212  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27213 <pre><code>
27214 var view = new Roo.JsonView({
27215     container: "my-element",
27216     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27217     multiSelect: true, 
27218     jsonRoot: "data" 
27219 });
27220
27221 // listen for node click?
27222 view.on("click", function(vw, index, node, e){
27223     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27224 });
27225
27226 // direct load of JSON data
27227 view.load("foobar.php");
27228
27229 // Example from my blog list
27230 var tpl = new Roo.Template(
27231     '&lt;div class="entry"&gt;' +
27232     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27233     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27234     "&lt;/div&gt;&lt;hr /&gt;"
27235 );
27236
27237 var moreView = new Roo.JsonView({
27238     container :  "entry-list", 
27239     template : tpl,
27240     jsonRoot: "posts"
27241 });
27242 moreView.on("beforerender", this.sortEntries, this);
27243 moreView.load({
27244     url: "/blog/get-posts.php",
27245     params: "allposts=true",
27246     text: "Loading Blog Entries..."
27247 });
27248 </code></pre>
27249
27250 * Note: old code is supported with arguments : (container, template, config)
27251
27252
27253  * @constructor
27254  * Create a new JsonView
27255  * 
27256  * @param {Object} config The config object
27257  * 
27258  */
27259 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27260     
27261     
27262     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27263
27264     var um = this.el.getUpdateManager();
27265     um.setRenderer(this);
27266     um.on("update", this.onLoad, this);
27267     um.on("failure", this.onLoadException, this);
27268
27269     /**
27270      * @event beforerender
27271      * Fires before rendering of the downloaded JSON data.
27272      * @param {Roo.JsonView} this
27273      * @param {Object} data The JSON data loaded
27274      */
27275     /**
27276      * @event load
27277      * Fires when data is loaded.
27278      * @param {Roo.JsonView} this
27279      * @param {Object} data The JSON data loaded
27280      * @param {Object} response The raw Connect response object
27281      */
27282     /**
27283      * @event loadexception
27284      * Fires when loading fails.
27285      * @param {Roo.JsonView} this
27286      * @param {Object} response The raw Connect response object
27287      */
27288     this.addEvents({
27289         'beforerender' : true,
27290         'load' : true,
27291         'loadexception' : true
27292     });
27293 };
27294 Roo.extend(Roo.JsonView, Roo.View, {
27295     /**
27296      * @type {String} The root property in the loaded JSON object that contains the data
27297      */
27298     jsonRoot : "",
27299
27300     /**
27301      * Refreshes the view.
27302      */
27303     refresh : function(){
27304         this.clearSelections();
27305         this.el.update("");
27306         var html = [];
27307         var o = this.jsonData;
27308         if(o && o.length > 0){
27309             for(var i = 0, len = o.length; i < len; i++){
27310                 var data = this.prepareData(o[i], i, o);
27311                 html[html.length] = this.tpl.apply(data);
27312             }
27313         }else{
27314             html.push(this.emptyText);
27315         }
27316         this.el.update(html.join(""));
27317         this.nodes = this.el.dom.childNodes;
27318         this.updateIndexes(0);
27319     },
27320
27321     /**
27322      * 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.
27323      * @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:
27324      <pre><code>
27325      view.load({
27326          url: "your-url.php",
27327          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27328          callback: yourFunction,
27329          scope: yourObject, //(optional scope)
27330          discardUrl: false,
27331          nocache: false,
27332          text: "Loading...",
27333          timeout: 30,
27334          scripts: false
27335      });
27336      </code></pre>
27337      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27338      * 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.
27339      * @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}
27340      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27341      * @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.
27342      */
27343     load : function(){
27344         var um = this.el.getUpdateManager();
27345         um.update.apply(um, arguments);
27346     },
27347
27348     // note - render is a standard framework call...
27349     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27350     render : function(el, response){
27351         
27352         this.clearSelections();
27353         this.el.update("");
27354         var o;
27355         try{
27356             if (response != '') {
27357                 o = Roo.util.JSON.decode(response.responseText);
27358                 if(this.jsonRoot){
27359                     
27360                     o = o[this.jsonRoot];
27361                 }
27362             }
27363         } catch(e){
27364         }
27365         /**
27366          * The current JSON data or null
27367          */
27368         this.jsonData = o;
27369         this.beforeRender();
27370         this.refresh();
27371     },
27372
27373 /**
27374  * Get the number of records in the current JSON dataset
27375  * @return {Number}
27376  */
27377     getCount : function(){
27378         return this.jsonData ? this.jsonData.length : 0;
27379     },
27380
27381 /**
27382  * Returns the JSON object for the specified node(s)
27383  * @param {HTMLElement/Array} node The node or an array of nodes
27384  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27385  * you get the JSON object for the node
27386  */
27387     getNodeData : function(node){
27388         if(node instanceof Array){
27389             var data = [];
27390             for(var i = 0, len = node.length; i < len; i++){
27391                 data.push(this.getNodeData(node[i]));
27392             }
27393             return data;
27394         }
27395         return this.jsonData[this.indexOf(node)] || null;
27396     },
27397
27398     beforeRender : function(){
27399         this.snapshot = this.jsonData;
27400         if(this.sortInfo){
27401             this.sort.apply(this, this.sortInfo);
27402         }
27403         this.fireEvent("beforerender", this, this.jsonData);
27404     },
27405
27406     onLoad : function(el, o){
27407         this.fireEvent("load", this, this.jsonData, o);
27408     },
27409
27410     onLoadException : function(el, o){
27411         this.fireEvent("loadexception", this, o);
27412     },
27413
27414 /**
27415  * Filter the data by a specific property.
27416  * @param {String} property A property on your JSON objects
27417  * @param {String/RegExp} value Either string that the property values
27418  * should start with, or a RegExp to test against the property
27419  */
27420     filter : function(property, value){
27421         if(this.jsonData){
27422             var data = [];
27423             var ss = this.snapshot;
27424             if(typeof value == "string"){
27425                 var vlen = value.length;
27426                 if(vlen == 0){
27427                     this.clearFilter();
27428                     return;
27429                 }
27430                 value = value.toLowerCase();
27431                 for(var i = 0, len = ss.length; i < len; i++){
27432                     var o = ss[i];
27433                     if(o[property].substr(0, vlen).toLowerCase() == value){
27434                         data.push(o);
27435                     }
27436                 }
27437             } else if(value.exec){ // regex?
27438                 for(var i = 0, len = ss.length; i < len; i++){
27439                     var o = ss[i];
27440                     if(value.test(o[property])){
27441                         data.push(o);
27442                     }
27443                 }
27444             } else{
27445                 return;
27446             }
27447             this.jsonData = data;
27448             this.refresh();
27449         }
27450     },
27451
27452 /**
27453  * Filter by a function. The passed function will be called with each
27454  * object in the current dataset. If the function returns true the value is kept,
27455  * otherwise it is filtered.
27456  * @param {Function} fn
27457  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27458  */
27459     filterBy : function(fn, scope){
27460         if(this.jsonData){
27461             var data = [];
27462             var ss = this.snapshot;
27463             for(var i = 0, len = ss.length; i < len; i++){
27464                 var o = ss[i];
27465                 if(fn.call(scope || this, o)){
27466                     data.push(o);
27467                 }
27468             }
27469             this.jsonData = data;
27470             this.refresh();
27471         }
27472     },
27473
27474 /**
27475  * Clears the current filter.
27476  */
27477     clearFilter : function(){
27478         if(this.snapshot && this.jsonData != this.snapshot){
27479             this.jsonData = this.snapshot;
27480             this.refresh();
27481         }
27482     },
27483
27484
27485 /**
27486  * Sorts the data for this view and refreshes it.
27487  * @param {String} property A property on your JSON objects to sort on
27488  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27489  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27490  */
27491     sort : function(property, dir, sortType){
27492         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27493         if(this.jsonData){
27494             var p = property;
27495             var dsc = dir && dir.toLowerCase() == "desc";
27496             var f = function(o1, o2){
27497                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27498                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27499                 ;
27500                 if(v1 < v2){
27501                     return dsc ? +1 : -1;
27502                 } else if(v1 > v2){
27503                     return dsc ? -1 : +1;
27504                 } else{
27505                     return 0;
27506                 }
27507             };
27508             this.jsonData.sort(f);
27509             this.refresh();
27510             if(this.jsonData != this.snapshot){
27511                 this.snapshot.sort(f);
27512             }
27513         }
27514     }
27515 });/*
27516  * Based on:
27517  * Ext JS Library 1.1.1
27518  * Copyright(c) 2006-2007, Ext JS, LLC.
27519  *
27520  * Originally Released Under LGPL - original licence link has changed is not relivant.
27521  *
27522  * Fork - LGPL
27523  * <script type="text/javascript">
27524  */
27525  
27526
27527 /**
27528  * @class Roo.ColorPalette
27529  * @extends Roo.Component
27530  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27531  * Here's an example of typical usage:
27532  * <pre><code>
27533 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27534 cp.render('my-div');
27535
27536 cp.on('select', function(palette, selColor){
27537     // do something with selColor
27538 });
27539 </code></pre>
27540  * @constructor
27541  * Create a new ColorPalette
27542  * @param {Object} config The config object
27543  */
27544 Roo.ColorPalette = function(config){
27545     Roo.ColorPalette.superclass.constructor.call(this, config);
27546     this.addEvents({
27547         /**
27548              * @event select
27549              * Fires when a color is selected
27550              * @param {ColorPalette} this
27551              * @param {String} color The 6-digit color hex code (without the # symbol)
27552              */
27553         select: true
27554     });
27555
27556     if(this.handler){
27557         this.on("select", this.handler, this.scope, true);
27558     }
27559 };
27560 Roo.extend(Roo.ColorPalette, Roo.Component, {
27561     /**
27562      * @cfg {String} itemCls
27563      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27564      */
27565     itemCls : "x-color-palette",
27566     /**
27567      * @cfg {String} value
27568      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27569      * the hex codes are case-sensitive.
27570      */
27571     value : null,
27572     clickEvent:'click',
27573     // private
27574     ctype: "Roo.ColorPalette",
27575
27576     /**
27577      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27578      */
27579     allowReselect : false,
27580
27581     /**
27582      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27583      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27584      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27585      * of colors with the width setting until the box is symmetrical.</p>
27586      * <p>You can override individual colors if needed:</p>
27587      * <pre><code>
27588 var cp = new Roo.ColorPalette();
27589 cp.colors[0] = "FF0000";  // change the first box to red
27590 </code></pre>
27591
27592 Or you can provide a custom array of your own for complete control:
27593 <pre><code>
27594 var cp = new Roo.ColorPalette();
27595 cp.colors = ["000000", "993300", "333300"];
27596 </code></pre>
27597      * @type Array
27598      */
27599     colors : [
27600         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27601         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27602         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27603         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27604         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27605     ],
27606
27607     // private
27608     onRender : function(container, position){
27609         var t = new Roo.MasterTemplate(
27610             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27611         );
27612         var c = this.colors;
27613         for(var i = 0, len = c.length; i < len; i++){
27614             t.add([c[i]]);
27615         }
27616         var el = document.createElement("div");
27617         el.className = this.itemCls;
27618         t.overwrite(el);
27619         container.dom.insertBefore(el, position);
27620         this.el = Roo.get(el);
27621         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27622         if(this.clickEvent != 'click'){
27623             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27624         }
27625     },
27626
27627     // private
27628     afterRender : function(){
27629         Roo.ColorPalette.superclass.afterRender.call(this);
27630         if(this.value){
27631             var s = this.value;
27632             this.value = null;
27633             this.select(s);
27634         }
27635     },
27636
27637     // private
27638     handleClick : function(e, t){
27639         e.preventDefault();
27640         if(!this.disabled){
27641             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27642             this.select(c.toUpperCase());
27643         }
27644     },
27645
27646     /**
27647      * Selects the specified color in the palette (fires the select event)
27648      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27649      */
27650     select : function(color){
27651         color = color.replace("#", "");
27652         if(color != this.value || this.allowReselect){
27653             var el = this.el;
27654             if(this.value){
27655                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27656             }
27657             el.child("a.color-"+color).addClass("x-color-palette-sel");
27658             this.value = color;
27659             this.fireEvent("select", this, color);
27660         }
27661     }
27662 });/*
27663  * Based on:
27664  * Ext JS Library 1.1.1
27665  * Copyright(c) 2006-2007, Ext JS, LLC.
27666  *
27667  * Originally Released Under LGPL - original licence link has changed is not relivant.
27668  *
27669  * Fork - LGPL
27670  * <script type="text/javascript">
27671  */
27672  
27673 /**
27674  * @class Roo.DatePicker
27675  * @extends Roo.Component
27676  * Simple date picker class.
27677  * @constructor
27678  * Create a new DatePicker
27679  * @param {Object} config The config object
27680  */
27681 Roo.DatePicker = function(config){
27682     Roo.DatePicker.superclass.constructor.call(this, config);
27683
27684     this.value = config && config.value ?
27685                  config.value.clearTime() : new Date().clearTime();
27686
27687     this.addEvents({
27688         /**
27689              * @event select
27690              * Fires when a date is selected
27691              * @param {DatePicker} this
27692              * @param {Date} date The selected date
27693              */
27694         'select': true,
27695         /**
27696              * @event monthchange
27697              * Fires when the displayed month changes 
27698              * @param {DatePicker} this
27699              * @param {Date} date The selected month
27700              */
27701         'monthchange': true
27702     });
27703
27704     if(this.handler){
27705         this.on("select", this.handler,  this.scope || this);
27706     }
27707     // build the disabledDatesRE
27708     if(!this.disabledDatesRE && this.disabledDates){
27709         var dd = this.disabledDates;
27710         var re = "(?:";
27711         for(var i = 0; i < dd.length; i++){
27712             re += dd[i];
27713             if(i != dd.length-1) {
27714                 re += "|";
27715             }
27716         }
27717         this.disabledDatesRE = new RegExp(re + ")");
27718     }
27719 };
27720
27721 Roo.extend(Roo.DatePicker, Roo.Component, {
27722     /**
27723      * @cfg {String} todayText
27724      * The text to display on the button that selects the current date (defaults to "Today")
27725      */
27726     todayText : "Today",
27727     /**
27728      * @cfg {String} okText
27729      * The text to display on the ok button
27730      */
27731     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27732     /**
27733      * @cfg {String} cancelText
27734      * The text to display on the cancel button
27735      */
27736     cancelText : "Cancel",
27737     /**
27738      * @cfg {String} todayTip
27739      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27740      */
27741     todayTip : "{0} (Spacebar)",
27742     /**
27743      * @cfg {Date} minDate
27744      * Minimum allowable date (JavaScript date object, defaults to null)
27745      */
27746     minDate : null,
27747     /**
27748      * @cfg {Date} maxDate
27749      * Maximum allowable date (JavaScript date object, defaults to null)
27750      */
27751     maxDate : null,
27752     /**
27753      * @cfg {String} minText
27754      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27755      */
27756     minText : "This date is before the minimum date",
27757     /**
27758      * @cfg {String} maxText
27759      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27760      */
27761     maxText : "This date is after the maximum date",
27762     /**
27763      * @cfg {String} format
27764      * The default date format string which can be overriden for localization support.  The format must be
27765      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27766      */
27767     format : "m/d/y",
27768     /**
27769      * @cfg {Array} disabledDays
27770      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27771      */
27772     disabledDays : null,
27773     /**
27774      * @cfg {String} disabledDaysText
27775      * The tooltip to display when the date falls on a disabled day (defaults to "")
27776      */
27777     disabledDaysText : "",
27778     /**
27779      * @cfg {RegExp} disabledDatesRE
27780      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27781      */
27782     disabledDatesRE : null,
27783     /**
27784      * @cfg {String} disabledDatesText
27785      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27786      */
27787     disabledDatesText : "",
27788     /**
27789      * @cfg {Boolean} constrainToViewport
27790      * True to constrain the date picker to the viewport (defaults to true)
27791      */
27792     constrainToViewport : true,
27793     /**
27794      * @cfg {Array} monthNames
27795      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27796      */
27797     monthNames : Date.monthNames,
27798     /**
27799      * @cfg {Array} dayNames
27800      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27801      */
27802     dayNames : Date.dayNames,
27803     /**
27804      * @cfg {String} nextText
27805      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27806      */
27807     nextText: 'Next Month (Control+Right)',
27808     /**
27809      * @cfg {String} prevText
27810      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27811      */
27812     prevText: 'Previous Month (Control+Left)',
27813     /**
27814      * @cfg {String} monthYearText
27815      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27816      */
27817     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27818     /**
27819      * @cfg {Number} startDay
27820      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27821      */
27822     startDay : 0,
27823     /**
27824      * @cfg {Bool} showClear
27825      * Show a clear button (usefull for date form elements that can be blank.)
27826      */
27827     
27828     showClear: false,
27829     
27830     /**
27831      * Sets the value of the date field
27832      * @param {Date} value The date to set
27833      */
27834     setValue : function(value){
27835         var old = this.value;
27836         
27837         if (typeof(value) == 'string') {
27838          
27839             value = Date.parseDate(value, this.format);
27840         }
27841         if (!value) {
27842             value = new Date();
27843         }
27844         
27845         this.value = value.clearTime(true);
27846         if(this.el){
27847             this.update(this.value);
27848         }
27849     },
27850
27851     /**
27852      * Gets the current selected value of the date field
27853      * @return {Date} The selected date
27854      */
27855     getValue : function(){
27856         return this.value;
27857     },
27858
27859     // private
27860     focus : function(){
27861         if(this.el){
27862             this.update(this.activeDate);
27863         }
27864     },
27865
27866     // privateval
27867     onRender : function(container, position){
27868         
27869         var m = [
27870              '<table cellspacing="0">',
27871                 '<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>',
27872                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27873         var dn = this.dayNames;
27874         for(var i = 0; i < 7; i++){
27875             var d = this.startDay+i;
27876             if(d > 6){
27877                 d = d-7;
27878             }
27879             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27880         }
27881         m[m.length] = "</tr></thead><tbody><tr>";
27882         for(var i = 0; i < 42; i++) {
27883             if(i % 7 == 0 && i != 0){
27884                 m[m.length] = "</tr><tr>";
27885             }
27886             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27887         }
27888         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27889             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27890
27891         var el = document.createElement("div");
27892         el.className = "x-date-picker";
27893         el.innerHTML = m.join("");
27894
27895         container.dom.insertBefore(el, position);
27896
27897         this.el = Roo.get(el);
27898         this.eventEl = Roo.get(el.firstChild);
27899
27900         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27901             handler: this.showPrevMonth,
27902             scope: this,
27903             preventDefault:true,
27904             stopDefault:true
27905         });
27906
27907         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27908             handler: this.showNextMonth,
27909             scope: this,
27910             preventDefault:true,
27911             stopDefault:true
27912         });
27913
27914         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27915
27916         this.monthPicker = this.el.down('div.x-date-mp');
27917         this.monthPicker.enableDisplayMode('block');
27918         
27919         var kn = new Roo.KeyNav(this.eventEl, {
27920             "left" : function(e){
27921                 e.ctrlKey ?
27922                     this.showPrevMonth() :
27923                     this.update(this.activeDate.add("d", -1));
27924             },
27925
27926             "right" : function(e){
27927                 e.ctrlKey ?
27928                     this.showNextMonth() :
27929                     this.update(this.activeDate.add("d", 1));
27930             },
27931
27932             "up" : function(e){
27933                 e.ctrlKey ?
27934                     this.showNextYear() :
27935                     this.update(this.activeDate.add("d", -7));
27936             },
27937
27938             "down" : function(e){
27939                 e.ctrlKey ?
27940                     this.showPrevYear() :
27941                     this.update(this.activeDate.add("d", 7));
27942             },
27943
27944             "pageUp" : function(e){
27945                 this.showNextMonth();
27946             },
27947
27948             "pageDown" : function(e){
27949                 this.showPrevMonth();
27950             },
27951
27952             "enter" : function(e){
27953                 e.stopPropagation();
27954                 return true;
27955             },
27956
27957             scope : this
27958         });
27959
27960         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27961
27962         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27963
27964         this.el.unselectable();
27965         
27966         this.cells = this.el.select("table.x-date-inner tbody td");
27967         this.textNodes = this.el.query("table.x-date-inner tbody span");
27968
27969         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27970             text: "&#160;",
27971             tooltip: this.monthYearText
27972         });
27973
27974         this.mbtn.on('click', this.showMonthPicker, this);
27975         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27976
27977
27978         var today = (new Date()).dateFormat(this.format);
27979         
27980         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27981         if (this.showClear) {
27982             baseTb.add( new Roo.Toolbar.Fill());
27983         }
27984         baseTb.add({
27985             text: String.format(this.todayText, today),
27986             tooltip: String.format(this.todayTip, today),
27987             handler: this.selectToday,
27988             scope: this
27989         });
27990         
27991         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27992             
27993         //});
27994         if (this.showClear) {
27995             
27996             baseTb.add( new Roo.Toolbar.Fill());
27997             baseTb.add({
27998                 text: '&#160;',
27999                 cls: 'x-btn-icon x-btn-clear',
28000                 handler: function() {
28001                     //this.value = '';
28002                     this.fireEvent("select", this, '');
28003                 },
28004                 scope: this
28005             });
28006         }
28007         
28008         
28009         if(Roo.isIE){
28010             this.el.repaint();
28011         }
28012         this.update(this.value);
28013     },
28014
28015     createMonthPicker : function(){
28016         if(!this.monthPicker.dom.firstChild){
28017             var buf = ['<table border="0" cellspacing="0">'];
28018             for(var i = 0; i < 6; i++){
28019                 buf.push(
28020                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28021                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28022                     i == 0 ?
28023                     '<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>' :
28024                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28025                 );
28026             }
28027             buf.push(
28028                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28029                     this.okText,
28030                     '</button><button type="button" class="x-date-mp-cancel">',
28031                     this.cancelText,
28032                     '</button></td></tr>',
28033                 '</table>'
28034             );
28035             this.monthPicker.update(buf.join(''));
28036             this.monthPicker.on('click', this.onMonthClick, this);
28037             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28038
28039             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28040             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28041
28042             this.mpMonths.each(function(m, a, i){
28043                 i += 1;
28044                 if((i%2) == 0){
28045                     m.dom.xmonth = 5 + Math.round(i * .5);
28046                 }else{
28047                     m.dom.xmonth = Math.round((i-1) * .5);
28048                 }
28049             });
28050         }
28051     },
28052
28053     showMonthPicker : function(){
28054         this.createMonthPicker();
28055         var size = this.el.getSize();
28056         this.monthPicker.setSize(size);
28057         this.monthPicker.child('table').setSize(size);
28058
28059         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28060         this.updateMPMonth(this.mpSelMonth);
28061         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28062         this.updateMPYear(this.mpSelYear);
28063
28064         this.monthPicker.slideIn('t', {duration:.2});
28065     },
28066
28067     updateMPYear : function(y){
28068         this.mpyear = y;
28069         var ys = this.mpYears.elements;
28070         for(var i = 1; i <= 10; i++){
28071             var td = ys[i-1], y2;
28072             if((i%2) == 0){
28073                 y2 = y + Math.round(i * .5);
28074                 td.firstChild.innerHTML = y2;
28075                 td.xyear = y2;
28076             }else{
28077                 y2 = y - (5-Math.round(i * .5));
28078                 td.firstChild.innerHTML = y2;
28079                 td.xyear = y2;
28080             }
28081             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28082         }
28083     },
28084
28085     updateMPMonth : function(sm){
28086         this.mpMonths.each(function(m, a, i){
28087             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28088         });
28089     },
28090
28091     selectMPMonth: function(m){
28092         
28093     },
28094
28095     onMonthClick : function(e, t){
28096         e.stopEvent();
28097         var el = new Roo.Element(t), pn;
28098         if(el.is('button.x-date-mp-cancel')){
28099             this.hideMonthPicker();
28100         }
28101         else if(el.is('button.x-date-mp-ok')){
28102             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28103             this.hideMonthPicker();
28104         }
28105         else if(pn = el.up('td.x-date-mp-month', 2)){
28106             this.mpMonths.removeClass('x-date-mp-sel');
28107             pn.addClass('x-date-mp-sel');
28108             this.mpSelMonth = pn.dom.xmonth;
28109         }
28110         else if(pn = el.up('td.x-date-mp-year', 2)){
28111             this.mpYears.removeClass('x-date-mp-sel');
28112             pn.addClass('x-date-mp-sel');
28113             this.mpSelYear = pn.dom.xyear;
28114         }
28115         else if(el.is('a.x-date-mp-prev')){
28116             this.updateMPYear(this.mpyear-10);
28117         }
28118         else if(el.is('a.x-date-mp-next')){
28119             this.updateMPYear(this.mpyear+10);
28120         }
28121     },
28122
28123     onMonthDblClick : function(e, t){
28124         e.stopEvent();
28125         var el = new Roo.Element(t), pn;
28126         if(pn = el.up('td.x-date-mp-month', 2)){
28127             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28128             this.hideMonthPicker();
28129         }
28130         else if(pn = el.up('td.x-date-mp-year', 2)){
28131             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28132             this.hideMonthPicker();
28133         }
28134     },
28135
28136     hideMonthPicker : function(disableAnim){
28137         if(this.monthPicker){
28138             if(disableAnim === true){
28139                 this.monthPicker.hide();
28140             }else{
28141                 this.monthPicker.slideOut('t', {duration:.2});
28142             }
28143         }
28144     },
28145
28146     // private
28147     showPrevMonth : function(e){
28148         this.update(this.activeDate.add("mo", -1));
28149     },
28150
28151     // private
28152     showNextMonth : function(e){
28153         this.update(this.activeDate.add("mo", 1));
28154     },
28155
28156     // private
28157     showPrevYear : function(){
28158         this.update(this.activeDate.add("y", -1));
28159     },
28160
28161     // private
28162     showNextYear : function(){
28163         this.update(this.activeDate.add("y", 1));
28164     },
28165
28166     // private
28167     handleMouseWheel : function(e){
28168         var delta = e.getWheelDelta();
28169         if(delta > 0){
28170             this.showPrevMonth();
28171             e.stopEvent();
28172         } else if(delta < 0){
28173             this.showNextMonth();
28174             e.stopEvent();
28175         }
28176     },
28177
28178     // private
28179     handleDateClick : function(e, t){
28180         e.stopEvent();
28181         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28182             this.setValue(new Date(t.dateValue));
28183             this.fireEvent("select", this, this.value);
28184         }
28185     },
28186
28187     // private
28188     selectToday : function(){
28189         this.setValue(new Date().clearTime());
28190         this.fireEvent("select", this, this.value);
28191     },
28192
28193     // private
28194     update : function(date)
28195     {
28196         var vd = this.activeDate;
28197         this.activeDate = date;
28198         if(vd && this.el){
28199             var t = date.getTime();
28200             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28201                 this.cells.removeClass("x-date-selected");
28202                 this.cells.each(function(c){
28203                    if(c.dom.firstChild.dateValue == t){
28204                        c.addClass("x-date-selected");
28205                        setTimeout(function(){
28206                             try{c.dom.firstChild.focus();}catch(e){}
28207                        }, 50);
28208                        return false;
28209                    }
28210                 });
28211                 return;
28212             }
28213         }
28214         
28215         var days = date.getDaysInMonth();
28216         var firstOfMonth = date.getFirstDateOfMonth();
28217         var startingPos = firstOfMonth.getDay()-this.startDay;
28218
28219         if(startingPos <= this.startDay){
28220             startingPos += 7;
28221         }
28222
28223         var pm = date.add("mo", -1);
28224         var prevStart = pm.getDaysInMonth()-startingPos;
28225
28226         var cells = this.cells.elements;
28227         var textEls = this.textNodes;
28228         days += startingPos;
28229
28230         // convert everything to numbers so it's fast
28231         var day = 86400000;
28232         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28233         var today = new Date().clearTime().getTime();
28234         var sel = date.clearTime().getTime();
28235         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28236         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28237         var ddMatch = this.disabledDatesRE;
28238         var ddText = this.disabledDatesText;
28239         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28240         var ddaysText = this.disabledDaysText;
28241         var format = this.format;
28242
28243         var setCellClass = function(cal, cell){
28244             cell.title = "";
28245             var t = d.getTime();
28246             cell.firstChild.dateValue = t;
28247             if(t == today){
28248                 cell.className += " x-date-today";
28249                 cell.title = cal.todayText;
28250             }
28251             if(t == sel){
28252                 cell.className += " x-date-selected";
28253                 setTimeout(function(){
28254                     try{cell.firstChild.focus();}catch(e){}
28255                 }, 50);
28256             }
28257             // disabling
28258             if(t < min) {
28259                 cell.className = " x-date-disabled";
28260                 cell.title = cal.minText;
28261                 return;
28262             }
28263             if(t > max) {
28264                 cell.className = " x-date-disabled";
28265                 cell.title = cal.maxText;
28266                 return;
28267             }
28268             if(ddays){
28269                 if(ddays.indexOf(d.getDay()) != -1){
28270                     cell.title = ddaysText;
28271                     cell.className = " x-date-disabled";
28272                 }
28273             }
28274             if(ddMatch && format){
28275                 var fvalue = d.dateFormat(format);
28276                 if(ddMatch.test(fvalue)){
28277                     cell.title = ddText.replace("%0", fvalue);
28278                     cell.className = " x-date-disabled";
28279                 }
28280             }
28281         };
28282
28283         var i = 0;
28284         for(; i < startingPos; i++) {
28285             textEls[i].innerHTML = (++prevStart);
28286             d.setDate(d.getDate()+1);
28287             cells[i].className = "x-date-prevday";
28288             setCellClass(this, cells[i]);
28289         }
28290         for(; i < days; i++){
28291             intDay = i - startingPos + 1;
28292             textEls[i].innerHTML = (intDay);
28293             d.setDate(d.getDate()+1);
28294             cells[i].className = "x-date-active";
28295             setCellClass(this, cells[i]);
28296         }
28297         var extraDays = 0;
28298         for(; i < 42; i++) {
28299              textEls[i].innerHTML = (++extraDays);
28300              d.setDate(d.getDate()+1);
28301              cells[i].className = "x-date-nextday";
28302              setCellClass(this, cells[i]);
28303         }
28304
28305         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28306         this.fireEvent('monthchange', this, date);
28307         
28308         if(!this.internalRender){
28309             var main = this.el.dom.firstChild;
28310             var w = main.offsetWidth;
28311             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28312             Roo.fly(main).setWidth(w);
28313             this.internalRender = true;
28314             // opera does not respect the auto grow header center column
28315             // then, after it gets a width opera refuses to recalculate
28316             // without a second pass
28317             if(Roo.isOpera && !this.secondPass){
28318                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28319                 this.secondPass = true;
28320                 this.update.defer(10, this, [date]);
28321             }
28322         }
28323         
28324         
28325     }
28326 });        /*
28327  * Based on:
28328  * Ext JS Library 1.1.1
28329  * Copyright(c) 2006-2007, Ext JS, LLC.
28330  *
28331  * Originally Released Under LGPL - original licence link has changed is not relivant.
28332  *
28333  * Fork - LGPL
28334  * <script type="text/javascript">
28335  */
28336 /**
28337  * @class Roo.TabPanel
28338  * @extends Roo.util.Observable
28339  * A lightweight tab container.
28340  * <br><br>
28341  * Usage:
28342  * <pre><code>
28343 // basic tabs 1, built from existing content
28344 var tabs = new Roo.TabPanel("tabs1");
28345 tabs.addTab("script", "View Script");
28346 tabs.addTab("markup", "View Markup");
28347 tabs.activate("script");
28348
28349 // more advanced tabs, built from javascript
28350 var jtabs = new Roo.TabPanel("jtabs");
28351 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28352
28353 // set up the UpdateManager
28354 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28355 var updater = tab2.getUpdateManager();
28356 updater.setDefaultUrl("ajax1.htm");
28357 tab2.on('activate', updater.refresh, updater, true);
28358
28359 // Use setUrl for Ajax loading
28360 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28361 tab3.setUrl("ajax2.htm", null, true);
28362
28363 // Disabled tab
28364 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28365 tab4.disable();
28366
28367 jtabs.activate("jtabs-1");
28368  * </code></pre>
28369  * @constructor
28370  * Create a new TabPanel.
28371  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28372  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28373  */
28374 Roo.TabPanel = function(container, config){
28375     /**
28376     * The container element for this TabPanel.
28377     * @type Roo.Element
28378     */
28379     this.el = Roo.get(container, true);
28380     if(config){
28381         if(typeof config == "boolean"){
28382             this.tabPosition = config ? "bottom" : "top";
28383         }else{
28384             Roo.apply(this, config);
28385         }
28386     }
28387     if(this.tabPosition == "bottom"){
28388         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28389         this.el.addClass("x-tabs-bottom");
28390     }
28391     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28392     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28393     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28394     if(Roo.isIE){
28395         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28396     }
28397     if(this.tabPosition != "bottom"){
28398         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28399          * @type Roo.Element
28400          */
28401         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28402         this.el.addClass("x-tabs-top");
28403     }
28404     this.items = [];
28405
28406     this.bodyEl.setStyle("position", "relative");
28407
28408     this.active = null;
28409     this.activateDelegate = this.activate.createDelegate(this);
28410
28411     this.addEvents({
28412         /**
28413          * @event tabchange
28414          * Fires when the active tab changes
28415          * @param {Roo.TabPanel} this
28416          * @param {Roo.TabPanelItem} activePanel The new active tab
28417          */
28418         "tabchange": true,
28419         /**
28420          * @event beforetabchange
28421          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28422          * @param {Roo.TabPanel} this
28423          * @param {Object} e Set cancel to true on this object to cancel the tab change
28424          * @param {Roo.TabPanelItem} tab The tab being changed to
28425          */
28426         "beforetabchange" : true
28427     });
28428
28429     Roo.EventManager.onWindowResize(this.onResize, this);
28430     this.cpad = this.el.getPadding("lr");
28431     this.hiddenCount = 0;
28432
28433
28434     // toolbar on the tabbar support...
28435     if (this.toolbar) {
28436         var tcfg = this.toolbar;
28437         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28438         this.toolbar = new Roo.Toolbar(tcfg);
28439         if (Roo.isSafari) {
28440             var tbl = tcfg.container.child('table', true);
28441             tbl.setAttribute('width', '100%');
28442         }
28443         
28444     }
28445    
28446
28447
28448     Roo.TabPanel.superclass.constructor.call(this);
28449 };
28450
28451 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28452     /*
28453      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28454      */
28455     tabPosition : "top",
28456     /*
28457      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28458      */
28459     currentTabWidth : 0,
28460     /*
28461      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28462      */
28463     minTabWidth : 40,
28464     /*
28465      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28466      */
28467     maxTabWidth : 250,
28468     /*
28469      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28470      */
28471     preferredTabWidth : 175,
28472     /*
28473      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28474      */
28475     resizeTabs : false,
28476     /*
28477      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28478      */
28479     monitorResize : true,
28480     /*
28481      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28482      */
28483     toolbar : false,
28484
28485     /**
28486      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28487      * @param {String} id The id of the div to use <b>or create</b>
28488      * @param {String} text The text for the tab
28489      * @param {String} content (optional) Content to put in the TabPanelItem body
28490      * @param {Boolean} closable (optional) True to create a close icon on the tab
28491      * @return {Roo.TabPanelItem} The created TabPanelItem
28492      */
28493     addTab : function(id, text, content, closable){
28494         var item = new Roo.TabPanelItem(this, id, text, closable);
28495         this.addTabItem(item);
28496         if(content){
28497             item.setContent(content);
28498         }
28499         return item;
28500     },
28501
28502     /**
28503      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28504      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28505      * @return {Roo.TabPanelItem}
28506      */
28507     getTab : function(id){
28508         return this.items[id];
28509     },
28510
28511     /**
28512      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28513      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28514      */
28515     hideTab : function(id){
28516         var t = this.items[id];
28517         if(!t.isHidden()){
28518            t.setHidden(true);
28519            this.hiddenCount++;
28520            this.autoSizeTabs();
28521         }
28522     },
28523
28524     /**
28525      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28526      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28527      */
28528     unhideTab : function(id){
28529         var t = this.items[id];
28530         if(t.isHidden()){
28531            t.setHidden(false);
28532            this.hiddenCount--;
28533            this.autoSizeTabs();
28534         }
28535     },
28536
28537     /**
28538      * Adds an existing {@link Roo.TabPanelItem}.
28539      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28540      */
28541     addTabItem : function(item){
28542         this.items[item.id] = item;
28543         this.items.push(item);
28544         if(this.resizeTabs){
28545            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28546            this.autoSizeTabs();
28547         }else{
28548             item.autoSize();
28549         }
28550     },
28551
28552     /**
28553      * Removes a {@link Roo.TabPanelItem}.
28554      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28555      */
28556     removeTab : function(id){
28557         var items = this.items;
28558         var tab = items[id];
28559         if(!tab) { return; }
28560         var index = items.indexOf(tab);
28561         if(this.active == tab && items.length > 1){
28562             var newTab = this.getNextAvailable(index);
28563             if(newTab) {
28564                 newTab.activate();
28565             }
28566         }
28567         this.stripEl.dom.removeChild(tab.pnode.dom);
28568         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28569             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28570         }
28571         items.splice(index, 1);
28572         delete this.items[tab.id];
28573         tab.fireEvent("close", tab);
28574         tab.purgeListeners();
28575         this.autoSizeTabs();
28576     },
28577
28578     getNextAvailable : function(start){
28579         var items = this.items;
28580         var index = start;
28581         // look for a next tab that will slide over to
28582         // replace the one being removed
28583         while(index < items.length){
28584             var item = items[++index];
28585             if(item && !item.isHidden()){
28586                 return item;
28587             }
28588         }
28589         // if one isn't found select the previous tab (on the left)
28590         index = start;
28591         while(index >= 0){
28592             var item = items[--index];
28593             if(item && !item.isHidden()){
28594                 return item;
28595             }
28596         }
28597         return null;
28598     },
28599
28600     /**
28601      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28602      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28603      */
28604     disableTab : function(id){
28605         var tab = this.items[id];
28606         if(tab && this.active != tab){
28607             tab.disable();
28608         }
28609     },
28610
28611     /**
28612      * Enables a {@link Roo.TabPanelItem} that is disabled.
28613      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28614      */
28615     enableTab : function(id){
28616         var tab = this.items[id];
28617         tab.enable();
28618     },
28619
28620     /**
28621      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28622      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28623      * @return {Roo.TabPanelItem} The TabPanelItem.
28624      */
28625     activate : function(id){
28626         var tab = this.items[id];
28627         if(!tab){
28628             return null;
28629         }
28630         if(tab == this.active || tab.disabled){
28631             return tab;
28632         }
28633         var e = {};
28634         this.fireEvent("beforetabchange", this, e, tab);
28635         if(e.cancel !== true && !tab.disabled){
28636             if(this.active){
28637                 this.active.hide();
28638             }
28639             this.active = this.items[id];
28640             this.active.show();
28641             this.fireEvent("tabchange", this, this.active);
28642         }
28643         return tab;
28644     },
28645
28646     /**
28647      * Gets the active {@link Roo.TabPanelItem}.
28648      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28649      */
28650     getActiveTab : function(){
28651         return this.active;
28652     },
28653
28654     /**
28655      * Updates the tab body element to fit the height of the container element
28656      * for overflow scrolling
28657      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28658      */
28659     syncHeight : function(targetHeight){
28660         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28661         var bm = this.bodyEl.getMargins();
28662         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28663         this.bodyEl.setHeight(newHeight);
28664         return newHeight;
28665     },
28666
28667     onResize : function(){
28668         if(this.monitorResize){
28669             this.autoSizeTabs();
28670         }
28671     },
28672
28673     /**
28674      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28675      */
28676     beginUpdate : function(){
28677         this.updating = true;
28678     },
28679
28680     /**
28681      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28682      */
28683     endUpdate : function(){
28684         this.updating = false;
28685         this.autoSizeTabs();
28686     },
28687
28688     /**
28689      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28690      */
28691     autoSizeTabs : function(){
28692         var count = this.items.length;
28693         var vcount = count - this.hiddenCount;
28694         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28695             return;
28696         }
28697         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28698         var availWidth = Math.floor(w / vcount);
28699         var b = this.stripBody;
28700         if(b.getWidth() > w){
28701             var tabs = this.items;
28702             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28703             if(availWidth < this.minTabWidth){
28704                 /*if(!this.sleft){    // incomplete scrolling code
28705                     this.createScrollButtons();
28706                 }
28707                 this.showScroll();
28708                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28709             }
28710         }else{
28711             if(this.currentTabWidth < this.preferredTabWidth){
28712                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28713             }
28714         }
28715     },
28716
28717     /**
28718      * Returns the number of tabs in this TabPanel.
28719      * @return {Number}
28720      */
28721      getCount : function(){
28722          return this.items.length;
28723      },
28724
28725     /**
28726      * Resizes all the tabs to the passed width
28727      * @param {Number} The new width
28728      */
28729     setTabWidth : function(width){
28730         this.currentTabWidth = width;
28731         for(var i = 0, len = this.items.length; i < len; i++) {
28732                 if(!this.items[i].isHidden()) {
28733                 this.items[i].setWidth(width);
28734             }
28735         }
28736     },
28737
28738     /**
28739      * Destroys this TabPanel
28740      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28741      */
28742     destroy : function(removeEl){
28743         Roo.EventManager.removeResizeListener(this.onResize, this);
28744         for(var i = 0, len = this.items.length; i < len; i++){
28745             this.items[i].purgeListeners();
28746         }
28747         if(removeEl === true){
28748             this.el.update("");
28749             this.el.remove();
28750         }
28751     }
28752 });
28753
28754 /**
28755  * @class Roo.TabPanelItem
28756  * @extends Roo.util.Observable
28757  * Represents an individual item (tab plus body) in a TabPanel.
28758  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28759  * @param {String} id The id of this TabPanelItem
28760  * @param {String} text The text for the tab of this TabPanelItem
28761  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28762  */
28763 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28764     /**
28765      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28766      * @type Roo.TabPanel
28767      */
28768     this.tabPanel = tabPanel;
28769     /**
28770      * The id for this TabPanelItem
28771      * @type String
28772      */
28773     this.id = id;
28774     /** @private */
28775     this.disabled = false;
28776     /** @private */
28777     this.text = text;
28778     /** @private */
28779     this.loaded = false;
28780     this.closable = closable;
28781
28782     /**
28783      * The body element for this TabPanelItem.
28784      * @type Roo.Element
28785      */
28786     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28787     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28788     this.bodyEl.setStyle("display", "block");
28789     this.bodyEl.setStyle("zoom", "1");
28790     this.hideAction();
28791
28792     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28793     /** @private */
28794     this.el = Roo.get(els.el, true);
28795     this.inner = Roo.get(els.inner, true);
28796     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28797     this.pnode = Roo.get(els.el.parentNode, true);
28798     this.el.on("mousedown", this.onTabMouseDown, this);
28799     this.el.on("click", this.onTabClick, this);
28800     /** @private */
28801     if(closable){
28802         var c = Roo.get(els.close, true);
28803         c.dom.title = this.closeText;
28804         c.addClassOnOver("close-over");
28805         c.on("click", this.closeClick, this);
28806      }
28807
28808     this.addEvents({
28809          /**
28810          * @event activate
28811          * Fires when this tab becomes the active tab.
28812          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28813          * @param {Roo.TabPanelItem} this
28814          */
28815         "activate": true,
28816         /**
28817          * @event beforeclose
28818          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28819          * @param {Roo.TabPanelItem} this
28820          * @param {Object} e Set cancel to true on this object to cancel the close.
28821          */
28822         "beforeclose": true,
28823         /**
28824          * @event close
28825          * Fires when this tab is closed.
28826          * @param {Roo.TabPanelItem} this
28827          */
28828          "close": true,
28829         /**
28830          * @event deactivate
28831          * Fires when this tab is no longer the active tab.
28832          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28833          * @param {Roo.TabPanelItem} this
28834          */
28835          "deactivate" : true
28836     });
28837     this.hidden = false;
28838
28839     Roo.TabPanelItem.superclass.constructor.call(this);
28840 };
28841
28842 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28843     purgeListeners : function(){
28844        Roo.util.Observable.prototype.purgeListeners.call(this);
28845        this.el.removeAllListeners();
28846     },
28847     /**
28848      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28849      */
28850     show : function(){
28851         this.pnode.addClass("on");
28852         this.showAction();
28853         if(Roo.isOpera){
28854             this.tabPanel.stripWrap.repaint();
28855         }
28856         this.fireEvent("activate", this.tabPanel, this);
28857     },
28858
28859     /**
28860      * Returns true if this tab is the active tab.
28861      * @return {Boolean}
28862      */
28863     isActive : function(){
28864         return this.tabPanel.getActiveTab() == this;
28865     },
28866
28867     /**
28868      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28869      */
28870     hide : function(){
28871         this.pnode.removeClass("on");
28872         this.hideAction();
28873         this.fireEvent("deactivate", this.tabPanel, this);
28874     },
28875
28876     hideAction : function(){
28877         this.bodyEl.hide();
28878         this.bodyEl.setStyle("position", "absolute");
28879         this.bodyEl.setLeft("-20000px");
28880         this.bodyEl.setTop("-20000px");
28881     },
28882
28883     showAction : function(){
28884         this.bodyEl.setStyle("position", "relative");
28885         this.bodyEl.setTop("");
28886         this.bodyEl.setLeft("");
28887         this.bodyEl.show();
28888     },
28889
28890     /**
28891      * Set the tooltip for the tab.
28892      * @param {String} tooltip The tab's tooltip
28893      */
28894     setTooltip : function(text){
28895         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28896             this.textEl.dom.qtip = text;
28897             this.textEl.dom.removeAttribute('title');
28898         }else{
28899             this.textEl.dom.title = text;
28900         }
28901     },
28902
28903     onTabClick : function(e){
28904         e.preventDefault();
28905         this.tabPanel.activate(this.id);
28906     },
28907
28908     onTabMouseDown : function(e){
28909         e.preventDefault();
28910         this.tabPanel.activate(this.id);
28911     },
28912
28913     getWidth : function(){
28914         return this.inner.getWidth();
28915     },
28916
28917     setWidth : function(width){
28918         var iwidth = width - this.pnode.getPadding("lr");
28919         this.inner.setWidth(iwidth);
28920         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28921         this.pnode.setWidth(width);
28922     },
28923
28924     /**
28925      * Show or hide the tab
28926      * @param {Boolean} hidden True to hide or false to show.
28927      */
28928     setHidden : function(hidden){
28929         this.hidden = hidden;
28930         this.pnode.setStyle("display", hidden ? "none" : "");
28931     },
28932
28933     /**
28934      * Returns true if this tab is "hidden"
28935      * @return {Boolean}
28936      */
28937     isHidden : function(){
28938         return this.hidden;
28939     },
28940
28941     /**
28942      * Returns the text for this tab
28943      * @return {String}
28944      */
28945     getText : function(){
28946         return this.text;
28947     },
28948
28949     autoSize : function(){
28950         //this.el.beginMeasure();
28951         this.textEl.setWidth(1);
28952         /*
28953          *  #2804 [new] Tabs in Roojs
28954          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28955          */
28956         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28957         //this.el.endMeasure();
28958     },
28959
28960     /**
28961      * Sets the text for the tab (Note: this also sets the tooltip text)
28962      * @param {String} text The tab's text and tooltip
28963      */
28964     setText : function(text){
28965         this.text = text;
28966         this.textEl.update(text);
28967         this.setTooltip(text);
28968         if(!this.tabPanel.resizeTabs){
28969             this.autoSize();
28970         }
28971     },
28972     /**
28973      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28974      */
28975     activate : function(){
28976         this.tabPanel.activate(this.id);
28977     },
28978
28979     /**
28980      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28981      */
28982     disable : function(){
28983         if(this.tabPanel.active != this){
28984             this.disabled = true;
28985             this.pnode.addClass("disabled");
28986         }
28987     },
28988
28989     /**
28990      * Enables this TabPanelItem if it was previously disabled.
28991      */
28992     enable : function(){
28993         this.disabled = false;
28994         this.pnode.removeClass("disabled");
28995     },
28996
28997     /**
28998      * Sets the content for this TabPanelItem.
28999      * @param {String} content The content
29000      * @param {Boolean} loadScripts true to look for and load scripts
29001      */
29002     setContent : function(content, loadScripts){
29003         this.bodyEl.update(content, loadScripts);
29004     },
29005
29006     /**
29007      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29008      * @return {Roo.UpdateManager} The UpdateManager
29009      */
29010     getUpdateManager : function(){
29011         return this.bodyEl.getUpdateManager();
29012     },
29013
29014     /**
29015      * Set a URL to be used to load the content for this TabPanelItem.
29016      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29017      * @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)
29018      * @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)
29019      * @return {Roo.UpdateManager} The UpdateManager
29020      */
29021     setUrl : function(url, params, loadOnce){
29022         if(this.refreshDelegate){
29023             this.un('activate', this.refreshDelegate);
29024         }
29025         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29026         this.on("activate", this.refreshDelegate);
29027         return this.bodyEl.getUpdateManager();
29028     },
29029
29030     /** @private */
29031     _handleRefresh : function(url, params, loadOnce){
29032         if(!loadOnce || !this.loaded){
29033             var updater = this.bodyEl.getUpdateManager();
29034             updater.update(url, params, this._setLoaded.createDelegate(this));
29035         }
29036     },
29037
29038     /**
29039      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29040      *   Will fail silently if the setUrl method has not been called.
29041      *   This does not activate the panel, just updates its content.
29042      */
29043     refresh : function(){
29044         if(this.refreshDelegate){
29045            this.loaded = false;
29046            this.refreshDelegate();
29047         }
29048     },
29049
29050     /** @private */
29051     _setLoaded : function(){
29052         this.loaded = true;
29053     },
29054
29055     /** @private */
29056     closeClick : function(e){
29057         var o = {};
29058         e.stopEvent();
29059         this.fireEvent("beforeclose", this, o);
29060         if(o.cancel !== true){
29061             this.tabPanel.removeTab(this.id);
29062         }
29063     },
29064     /**
29065      * The text displayed in the tooltip for the close icon.
29066      * @type String
29067      */
29068     closeText : "Close this tab"
29069 });
29070
29071 /** @private */
29072 Roo.TabPanel.prototype.createStrip = function(container){
29073     var strip = document.createElement("div");
29074     strip.className = "x-tabs-wrap";
29075     container.appendChild(strip);
29076     return strip;
29077 };
29078 /** @private */
29079 Roo.TabPanel.prototype.createStripList = function(strip){
29080     // div wrapper for retard IE
29081     // returns the "tr" element.
29082     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29083         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29084         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29085     return strip.firstChild.firstChild.firstChild.firstChild;
29086 };
29087 /** @private */
29088 Roo.TabPanel.prototype.createBody = function(container){
29089     var body = document.createElement("div");
29090     Roo.id(body, "tab-body");
29091     Roo.fly(body).addClass("x-tabs-body");
29092     container.appendChild(body);
29093     return body;
29094 };
29095 /** @private */
29096 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29097     var body = Roo.getDom(id);
29098     if(!body){
29099         body = document.createElement("div");
29100         body.id = id;
29101     }
29102     Roo.fly(body).addClass("x-tabs-item-body");
29103     bodyEl.insertBefore(body, bodyEl.firstChild);
29104     return body;
29105 };
29106 /** @private */
29107 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29108     var td = document.createElement("td");
29109     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29110     //stripEl.appendChild(td);
29111     if(closable){
29112         td.className = "x-tabs-closable";
29113         if(!this.closeTpl){
29114             this.closeTpl = new Roo.Template(
29115                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29116                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29117                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29118             );
29119         }
29120         var el = this.closeTpl.overwrite(td, {"text": text});
29121         var close = el.getElementsByTagName("div")[0];
29122         var inner = el.getElementsByTagName("em")[0];
29123         return {"el": el, "close": close, "inner": inner};
29124     } else {
29125         if(!this.tabTpl){
29126             this.tabTpl = new Roo.Template(
29127                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29128                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29129             );
29130         }
29131         var el = this.tabTpl.overwrite(td, {"text": text});
29132         var inner = el.getElementsByTagName("em")[0];
29133         return {"el": el, "inner": inner};
29134     }
29135 };/*
29136  * Based on:
29137  * Ext JS Library 1.1.1
29138  * Copyright(c) 2006-2007, Ext JS, LLC.
29139  *
29140  * Originally Released Under LGPL - original licence link has changed is not relivant.
29141  *
29142  * Fork - LGPL
29143  * <script type="text/javascript">
29144  */
29145
29146 /**
29147  * @class Roo.Button
29148  * @extends Roo.util.Observable
29149  * Simple Button class
29150  * @cfg {String} text The button text
29151  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29152  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29153  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29154  * @cfg {Object} scope The scope of the handler
29155  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29156  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29157  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29158  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29159  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29160  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29161    applies if enableToggle = true)
29162  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29163  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29164   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29165  * @constructor
29166  * Create a new button
29167  * @param {Object} config The config object
29168  */
29169 Roo.Button = function(renderTo, config)
29170 {
29171     if (!config) {
29172         config = renderTo;
29173         renderTo = config.renderTo || false;
29174     }
29175     
29176     Roo.apply(this, config);
29177     this.addEvents({
29178         /**
29179              * @event click
29180              * Fires when this button is clicked
29181              * @param {Button} this
29182              * @param {EventObject} e The click event
29183              */
29184             "click" : true,
29185         /**
29186              * @event toggle
29187              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29188              * @param {Button} this
29189              * @param {Boolean} pressed
29190              */
29191             "toggle" : true,
29192         /**
29193              * @event mouseover
29194              * Fires when the mouse hovers over the button
29195              * @param {Button} this
29196              * @param {Event} e The event object
29197              */
29198         'mouseover' : true,
29199         /**
29200              * @event mouseout
29201              * Fires when the mouse exits the button
29202              * @param {Button} this
29203              * @param {Event} e The event object
29204              */
29205         'mouseout': true,
29206          /**
29207              * @event render
29208              * Fires when the button is rendered
29209              * @param {Button} this
29210              */
29211         'render': true
29212     });
29213     if(this.menu){
29214         this.menu = Roo.menu.MenuMgr.get(this.menu);
29215     }
29216     // register listeners first!!  - so render can be captured..
29217     Roo.util.Observable.call(this);
29218     if(renderTo){
29219         this.render(renderTo);
29220     }
29221     
29222   
29223 };
29224
29225 Roo.extend(Roo.Button, Roo.util.Observable, {
29226     /**
29227      * 
29228      */
29229     
29230     /**
29231      * Read-only. True if this button is hidden
29232      * @type Boolean
29233      */
29234     hidden : false,
29235     /**
29236      * Read-only. True if this button is disabled
29237      * @type Boolean
29238      */
29239     disabled : false,
29240     /**
29241      * Read-only. True if this button is pressed (only if enableToggle = true)
29242      * @type Boolean
29243      */
29244     pressed : false,
29245
29246     /**
29247      * @cfg {Number} tabIndex 
29248      * The DOM tabIndex for this button (defaults to undefined)
29249      */
29250     tabIndex : undefined,
29251
29252     /**
29253      * @cfg {Boolean} enableToggle
29254      * True to enable pressed/not pressed toggling (defaults to false)
29255      */
29256     enableToggle: false,
29257     /**
29258      * @cfg {Mixed} menu
29259      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29260      */
29261     menu : undefined,
29262     /**
29263      * @cfg {String} menuAlign
29264      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29265      */
29266     menuAlign : "tl-bl?",
29267
29268     /**
29269      * @cfg {String} iconCls
29270      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29271      */
29272     iconCls : undefined,
29273     /**
29274      * @cfg {String} type
29275      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29276      */
29277     type : 'button',
29278
29279     // private
29280     menuClassTarget: 'tr',
29281
29282     /**
29283      * @cfg {String} clickEvent
29284      * The type of event to map to the button's event handler (defaults to 'click')
29285      */
29286     clickEvent : 'click',
29287
29288     /**
29289      * @cfg {Boolean} handleMouseEvents
29290      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29291      */
29292     handleMouseEvents : true,
29293
29294     /**
29295      * @cfg {String} tooltipType
29296      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29297      */
29298     tooltipType : 'qtip',
29299
29300     /**
29301      * @cfg {String} cls
29302      * A CSS class to apply to the button's main element.
29303      */
29304     
29305     /**
29306      * @cfg {Roo.Template} template (Optional)
29307      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29308      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29309      * require code modifications if required elements (e.g. a button) aren't present.
29310      */
29311
29312     // private
29313     render : function(renderTo){
29314         var btn;
29315         if(this.hideParent){
29316             this.parentEl = Roo.get(renderTo);
29317         }
29318         if(!this.dhconfig){
29319             if(!this.template){
29320                 if(!Roo.Button.buttonTemplate){
29321                     // hideous table template
29322                     Roo.Button.buttonTemplate = new Roo.Template(
29323                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29324                         '<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>',
29325                         "</tr></tbody></table>");
29326                 }
29327                 this.template = Roo.Button.buttonTemplate;
29328             }
29329             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29330             var btnEl = btn.child("button:first");
29331             btnEl.on('focus', this.onFocus, this);
29332             btnEl.on('blur', this.onBlur, this);
29333             if(this.cls){
29334                 btn.addClass(this.cls);
29335             }
29336             if(this.icon){
29337                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29338             }
29339             if(this.iconCls){
29340                 btnEl.addClass(this.iconCls);
29341                 if(!this.cls){
29342                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29343                 }
29344             }
29345             if(this.tabIndex !== undefined){
29346                 btnEl.dom.tabIndex = this.tabIndex;
29347             }
29348             if(this.tooltip){
29349                 if(typeof this.tooltip == 'object'){
29350                     Roo.QuickTips.tips(Roo.apply({
29351                           target: btnEl.id
29352                     }, this.tooltip));
29353                 } else {
29354                     btnEl.dom[this.tooltipType] = this.tooltip;
29355                 }
29356             }
29357         }else{
29358             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29359         }
29360         this.el = btn;
29361         if(this.id){
29362             this.el.dom.id = this.el.id = this.id;
29363         }
29364         if(this.menu){
29365             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29366             this.menu.on("show", this.onMenuShow, this);
29367             this.menu.on("hide", this.onMenuHide, this);
29368         }
29369         btn.addClass("x-btn");
29370         if(Roo.isIE && !Roo.isIE7){
29371             this.autoWidth.defer(1, this);
29372         }else{
29373             this.autoWidth();
29374         }
29375         if(this.handleMouseEvents){
29376             btn.on("mouseover", this.onMouseOver, this);
29377             btn.on("mouseout", this.onMouseOut, this);
29378             btn.on("mousedown", this.onMouseDown, this);
29379         }
29380         btn.on(this.clickEvent, this.onClick, this);
29381         //btn.on("mouseup", this.onMouseUp, this);
29382         if(this.hidden){
29383             this.hide();
29384         }
29385         if(this.disabled){
29386             this.disable();
29387         }
29388         Roo.ButtonToggleMgr.register(this);
29389         if(this.pressed){
29390             this.el.addClass("x-btn-pressed");
29391         }
29392         if(this.repeat){
29393             var repeater = new Roo.util.ClickRepeater(btn,
29394                 typeof this.repeat == "object" ? this.repeat : {}
29395             );
29396             repeater.on("click", this.onClick,  this);
29397         }
29398         
29399         this.fireEvent('render', this);
29400         
29401     },
29402     /**
29403      * Returns the button's underlying element
29404      * @return {Roo.Element} The element
29405      */
29406     getEl : function(){
29407         return this.el;  
29408     },
29409     
29410     /**
29411      * Destroys this Button and removes any listeners.
29412      */
29413     destroy : function(){
29414         Roo.ButtonToggleMgr.unregister(this);
29415         this.el.removeAllListeners();
29416         this.purgeListeners();
29417         this.el.remove();
29418     },
29419
29420     // private
29421     autoWidth : function(){
29422         if(this.el){
29423             this.el.setWidth("auto");
29424             if(Roo.isIE7 && Roo.isStrict){
29425                 var ib = this.el.child('button');
29426                 if(ib && ib.getWidth() > 20){
29427                     ib.clip();
29428                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29429                 }
29430             }
29431             if(this.minWidth){
29432                 if(this.hidden){
29433                     this.el.beginMeasure();
29434                 }
29435                 if(this.el.getWidth() < this.minWidth){
29436                     this.el.setWidth(this.minWidth);
29437                 }
29438                 if(this.hidden){
29439                     this.el.endMeasure();
29440                 }
29441             }
29442         }
29443     },
29444
29445     /**
29446      * Assigns this button's click handler
29447      * @param {Function} handler The function to call when the button is clicked
29448      * @param {Object} scope (optional) Scope for the function passed in
29449      */
29450     setHandler : function(handler, scope){
29451         this.handler = handler;
29452         this.scope = scope;  
29453     },
29454     
29455     /**
29456      * Sets this button's text
29457      * @param {String} text The button text
29458      */
29459     setText : function(text){
29460         this.text = text;
29461         if(this.el){
29462             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29463         }
29464         this.autoWidth();
29465     },
29466     
29467     /**
29468      * Gets the text for this button
29469      * @return {String} The button text
29470      */
29471     getText : function(){
29472         return this.text;  
29473     },
29474     
29475     /**
29476      * Show this button
29477      */
29478     show: function(){
29479         this.hidden = false;
29480         if(this.el){
29481             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29482         }
29483     },
29484     
29485     /**
29486      * Hide this button
29487      */
29488     hide: function(){
29489         this.hidden = true;
29490         if(this.el){
29491             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29492         }
29493     },
29494     
29495     /**
29496      * Convenience function for boolean show/hide
29497      * @param {Boolean} visible True to show, false to hide
29498      */
29499     setVisible: function(visible){
29500         if(visible) {
29501             this.show();
29502         }else{
29503             this.hide();
29504         }
29505     },
29506     
29507     /**
29508      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29509      * @param {Boolean} state (optional) Force a particular state
29510      */
29511     toggle : function(state){
29512         state = state === undefined ? !this.pressed : state;
29513         if(state != this.pressed){
29514             if(state){
29515                 this.el.addClass("x-btn-pressed");
29516                 this.pressed = true;
29517                 this.fireEvent("toggle", this, true);
29518             }else{
29519                 this.el.removeClass("x-btn-pressed");
29520                 this.pressed = false;
29521                 this.fireEvent("toggle", this, false);
29522             }
29523             if(this.toggleHandler){
29524                 this.toggleHandler.call(this.scope || this, this, state);
29525             }
29526         }
29527     },
29528     
29529     /**
29530      * Focus the button
29531      */
29532     focus : function(){
29533         this.el.child('button:first').focus();
29534     },
29535     
29536     /**
29537      * Disable this button
29538      */
29539     disable : function(){
29540         if(this.el){
29541             this.el.addClass("x-btn-disabled");
29542         }
29543         this.disabled = true;
29544     },
29545     
29546     /**
29547      * Enable this button
29548      */
29549     enable : function(){
29550         if(this.el){
29551             this.el.removeClass("x-btn-disabled");
29552         }
29553         this.disabled = false;
29554     },
29555
29556     /**
29557      * Convenience function for boolean enable/disable
29558      * @param {Boolean} enabled True to enable, false to disable
29559      */
29560     setDisabled : function(v){
29561         this[v !== true ? "enable" : "disable"]();
29562     },
29563
29564     // private
29565     onClick : function(e)
29566     {
29567         if(e){
29568             e.preventDefault();
29569         }
29570         if(e.button != 0){
29571             return;
29572         }
29573         if(!this.disabled){
29574             if(this.enableToggle){
29575                 this.toggle();
29576             }
29577             if(this.menu && !this.menu.isVisible()){
29578                 this.menu.show(this.el, this.menuAlign);
29579             }
29580             this.fireEvent("click", this, e);
29581             if(this.handler){
29582                 this.el.removeClass("x-btn-over");
29583                 this.handler.call(this.scope || this, this, e);
29584             }
29585         }
29586     },
29587     // private
29588     onMouseOver : function(e){
29589         if(!this.disabled){
29590             this.el.addClass("x-btn-over");
29591             this.fireEvent('mouseover', this, e);
29592         }
29593     },
29594     // private
29595     onMouseOut : function(e){
29596         if(!e.within(this.el,  true)){
29597             this.el.removeClass("x-btn-over");
29598             this.fireEvent('mouseout', this, e);
29599         }
29600     },
29601     // private
29602     onFocus : function(e){
29603         if(!this.disabled){
29604             this.el.addClass("x-btn-focus");
29605         }
29606     },
29607     // private
29608     onBlur : function(e){
29609         this.el.removeClass("x-btn-focus");
29610     },
29611     // private
29612     onMouseDown : function(e){
29613         if(!this.disabled && e.button == 0){
29614             this.el.addClass("x-btn-click");
29615             Roo.get(document).on('mouseup', this.onMouseUp, this);
29616         }
29617     },
29618     // private
29619     onMouseUp : function(e){
29620         if(e.button == 0){
29621             this.el.removeClass("x-btn-click");
29622             Roo.get(document).un('mouseup', this.onMouseUp, this);
29623         }
29624     },
29625     // private
29626     onMenuShow : function(e){
29627         this.el.addClass("x-btn-menu-active");
29628     },
29629     // private
29630     onMenuHide : function(e){
29631         this.el.removeClass("x-btn-menu-active");
29632     }   
29633 });
29634
29635 // Private utility class used by Button
29636 Roo.ButtonToggleMgr = function(){
29637    var groups = {};
29638    
29639    function toggleGroup(btn, state){
29640        if(state){
29641            var g = groups[btn.toggleGroup];
29642            for(var i = 0, l = g.length; i < l; i++){
29643                if(g[i] != btn){
29644                    g[i].toggle(false);
29645                }
29646            }
29647        }
29648    }
29649    
29650    return {
29651        register : function(btn){
29652            if(!btn.toggleGroup){
29653                return;
29654            }
29655            var g = groups[btn.toggleGroup];
29656            if(!g){
29657                g = groups[btn.toggleGroup] = [];
29658            }
29659            g.push(btn);
29660            btn.on("toggle", toggleGroup);
29661        },
29662        
29663        unregister : function(btn){
29664            if(!btn.toggleGroup){
29665                return;
29666            }
29667            var g = groups[btn.toggleGroup];
29668            if(g){
29669                g.remove(btn);
29670                btn.un("toggle", toggleGroup);
29671            }
29672        }
29673    };
29674 }();/*
29675  * Based on:
29676  * Ext JS Library 1.1.1
29677  * Copyright(c) 2006-2007, Ext JS, LLC.
29678  *
29679  * Originally Released Under LGPL - original licence link has changed is not relivant.
29680  *
29681  * Fork - LGPL
29682  * <script type="text/javascript">
29683  */
29684  
29685 /**
29686  * @class Roo.SplitButton
29687  * @extends Roo.Button
29688  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29689  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29690  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29691  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29692  * @cfg {String} arrowTooltip The title attribute of the arrow
29693  * @constructor
29694  * Create a new menu button
29695  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29696  * @param {Object} config The config object
29697  */
29698 Roo.SplitButton = function(renderTo, config){
29699     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29700     /**
29701      * @event arrowclick
29702      * Fires when this button's arrow is clicked
29703      * @param {SplitButton} this
29704      * @param {EventObject} e The click event
29705      */
29706     this.addEvents({"arrowclick":true});
29707 };
29708
29709 Roo.extend(Roo.SplitButton, Roo.Button, {
29710     render : function(renderTo){
29711         // this is one sweet looking template!
29712         var tpl = new Roo.Template(
29713             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29714             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29715             '<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>',
29716             "</tbody></table></td><td>",
29717             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29718             '<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>',
29719             "</tbody></table></td></tr></table>"
29720         );
29721         var btn = tpl.append(renderTo, [this.text, this.type], true);
29722         var btnEl = btn.child("button");
29723         if(this.cls){
29724             btn.addClass(this.cls);
29725         }
29726         if(this.icon){
29727             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29728         }
29729         if(this.iconCls){
29730             btnEl.addClass(this.iconCls);
29731             if(!this.cls){
29732                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29733             }
29734         }
29735         this.el = btn;
29736         if(this.handleMouseEvents){
29737             btn.on("mouseover", this.onMouseOver, this);
29738             btn.on("mouseout", this.onMouseOut, this);
29739             btn.on("mousedown", this.onMouseDown, this);
29740             btn.on("mouseup", this.onMouseUp, this);
29741         }
29742         btn.on(this.clickEvent, this.onClick, this);
29743         if(this.tooltip){
29744             if(typeof this.tooltip == 'object'){
29745                 Roo.QuickTips.tips(Roo.apply({
29746                       target: btnEl.id
29747                 }, this.tooltip));
29748             } else {
29749                 btnEl.dom[this.tooltipType] = this.tooltip;
29750             }
29751         }
29752         if(this.arrowTooltip){
29753             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29754         }
29755         if(this.hidden){
29756             this.hide();
29757         }
29758         if(this.disabled){
29759             this.disable();
29760         }
29761         if(this.pressed){
29762             this.el.addClass("x-btn-pressed");
29763         }
29764         if(Roo.isIE && !Roo.isIE7){
29765             this.autoWidth.defer(1, this);
29766         }else{
29767             this.autoWidth();
29768         }
29769         if(this.menu){
29770             this.menu.on("show", this.onMenuShow, this);
29771             this.menu.on("hide", this.onMenuHide, this);
29772         }
29773         this.fireEvent('render', this);
29774     },
29775
29776     // private
29777     autoWidth : function(){
29778         if(this.el){
29779             var tbl = this.el.child("table:first");
29780             var tbl2 = this.el.child("table:last");
29781             this.el.setWidth("auto");
29782             tbl.setWidth("auto");
29783             if(Roo.isIE7 && Roo.isStrict){
29784                 var ib = this.el.child('button:first');
29785                 if(ib && ib.getWidth() > 20){
29786                     ib.clip();
29787                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29788                 }
29789             }
29790             if(this.minWidth){
29791                 if(this.hidden){
29792                     this.el.beginMeasure();
29793                 }
29794                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29795                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29796                 }
29797                 if(this.hidden){
29798                     this.el.endMeasure();
29799                 }
29800             }
29801             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29802         } 
29803     },
29804     /**
29805      * Sets this button's click handler
29806      * @param {Function} handler The function to call when the button is clicked
29807      * @param {Object} scope (optional) Scope for the function passed above
29808      */
29809     setHandler : function(handler, scope){
29810         this.handler = handler;
29811         this.scope = scope;  
29812     },
29813     
29814     /**
29815      * Sets this button's arrow click handler
29816      * @param {Function} handler The function to call when the arrow is clicked
29817      * @param {Object} scope (optional) Scope for the function passed above
29818      */
29819     setArrowHandler : function(handler, scope){
29820         this.arrowHandler = handler;
29821         this.scope = scope;  
29822     },
29823     
29824     /**
29825      * Focus the button
29826      */
29827     focus : function(){
29828         if(this.el){
29829             this.el.child("button:first").focus();
29830         }
29831     },
29832
29833     // private
29834     onClick : function(e){
29835         e.preventDefault();
29836         if(!this.disabled){
29837             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29838                 if(this.menu && !this.menu.isVisible()){
29839                     this.menu.show(this.el, this.menuAlign);
29840                 }
29841                 this.fireEvent("arrowclick", this, e);
29842                 if(this.arrowHandler){
29843                     this.arrowHandler.call(this.scope || this, this, e);
29844                 }
29845             }else{
29846                 this.fireEvent("click", this, e);
29847                 if(this.handler){
29848                     this.handler.call(this.scope || this, this, e);
29849                 }
29850             }
29851         }
29852     },
29853     // private
29854     onMouseDown : function(e){
29855         if(!this.disabled){
29856             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29857         }
29858     },
29859     // private
29860     onMouseUp : function(e){
29861         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29862     }   
29863 });
29864
29865
29866 // backwards compat
29867 Roo.MenuButton = Roo.SplitButton;/*
29868  * Based on:
29869  * Ext JS Library 1.1.1
29870  * Copyright(c) 2006-2007, Ext JS, LLC.
29871  *
29872  * Originally Released Under LGPL - original licence link has changed is not relivant.
29873  *
29874  * Fork - LGPL
29875  * <script type="text/javascript">
29876  */
29877
29878 /**
29879  * @class Roo.Toolbar
29880  * Basic Toolbar class.
29881  * @constructor
29882  * Creates a new Toolbar
29883  * @param {Object} container The config object
29884  */ 
29885 Roo.Toolbar = function(container, buttons, config)
29886 {
29887     /// old consturctor format still supported..
29888     if(container instanceof Array){ // omit the container for later rendering
29889         buttons = container;
29890         config = buttons;
29891         container = null;
29892     }
29893     if (typeof(container) == 'object' && container.xtype) {
29894         config = container;
29895         container = config.container;
29896         buttons = config.buttons || []; // not really - use items!!
29897     }
29898     var xitems = [];
29899     if (config && config.items) {
29900         xitems = config.items;
29901         delete config.items;
29902     }
29903     Roo.apply(this, config);
29904     this.buttons = buttons;
29905     
29906     if(container){
29907         this.render(container);
29908     }
29909     this.xitems = xitems;
29910     Roo.each(xitems, function(b) {
29911         this.add(b);
29912     }, this);
29913     
29914 };
29915
29916 Roo.Toolbar.prototype = {
29917     /**
29918      * @cfg {Array} items
29919      * array of button configs or elements to add (will be converted to a MixedCollection)
29920      */
29921     
29922     /**
29923      * @cfg {String/HTMLElement/Element} container
29924      * The id or element that will contain the toolbar
29925      */
29926     // private
29927     render : function(ct){
29928         this.el = Roo.get(ct);
29929         if(this.cls){
29930             this.el.addClass(this.cls);
29931         }
29932         // using a table allows for vertical alignment
29933         // 100% width is needed by Safari...
29934         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29935         this.tr = this.el.child("tr", true);
29936         var autoId = 0;
29937         this.items = new Roo.util.MixedCollection(false, function(o){
29938             return o.id || ("item" + (++autoId));
29939         });
29940         if(this.buttons){
29941             this.add.apply(this, this.buttons);
29942             delete this.buttons;
29943         }
29944     },
29945
29946     /**
29947      * Adds element(s) to the toolbar -- this function takes a variable number of 
29948      * arguments of mixed type and adds them to the toolbar.
29949      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29950      * <ul>
29951      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29952      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29953      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29954      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29955      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29956      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29957      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29958      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29959      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29960      * </ul>
29961      * @param {Mixed} arg2
29962      * @param {Mixed} etc.
29963      */
29964     add : function(){
29965         var a = arguments, l = a.length;
29966         for(var i = 0; i < l; i++){
29967             this._add(a[i]);
29968         }
29969     },
29970     // private..
29971     _add : function(el) {
29972         
29973         if (el.xtype) {
29974             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29975         }
29976         
29977         if (el.applyTo){ // some kind of form field
29978             return this.addField(el);
29979         } 
29980         if (el.render){ // some kind of Toolbar.Item
29981             return this.addItem(el);
29982         }
29983         if (typeof el == "string"){ // string
29984             if(el == "separator" || el == "-"){
29985                 return this.addSeparator();
29986             }
29987             if (el == " "){
29988                 return this.addSpacer();
29989             }
29990             if(el == "->"){
29991                 return this.addFill();
29992             }
29993             return this.addText(el);
29994             
29995         }
29996         if(el.tagName){ // element
29997             return this.addElement(el);
29998         }
29999         if(typeof el == "object"){ // must be button config?
30000             return this.addButton(el);
30001         }
30002         // and now what?!?!
30003         return false;
30004         
30005     },
30006     
30007     /**
30008      * Add an Xtype element
30009      * @param {Object} xtype Xtype Object
30010      * @return {Object} created Object
30011      */
30012     addxtype : function(e){
30013         return this.add(e);  
30014     },
30015     
30016     /**
30017      * Returns the Element for this toolbar.
30018      * @return {Roo.Element}
30019      */
30020     getEl : function(){
30021         return this.el;  
30022     },
30023     
30024     /**
30025      * Adds a separator
30026      * @return {Roo.Toolbar.Item} The separator item
30027      */
30028     addSeparator : function(){
30029         return this.addItem(new Roo.Toolbar.Separator());
30030     },
30031
30032     /**
30033      * Adds a spacer element
30034      * @return {Roo.Toolbar.Spacer} The spacer item
30035      */
30036     addSpacer : function(){
30037         return this.addItem(new Roo.Toolbar.Spacer());
30038     },
30039
30040     /**
30041      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30042      * @return {Roo.Toolbar.Fill} The fill item
30043      */
30044     addFill : function(){
30045         return this.addItem(new Roo.Toolbar.Fill());
30046     },
30047
30048     /**
30049      * Adds any standard HTML element to the toolbar
30050      * @param {String/HTMLElement/Element} el The element or id of the element to add
30051      * @return {Roo.Toolbar.Item} The element's item
30052      */
30053     addElement : function(el){
30054         return this.addItem(new Roo.Toolbar.Item(el));
30055     },
30056     /**
30057      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30058      * @type Roo.util.MixedCollection  
30059      */
30060     items : false,
30061      
30062     /**
30063      * Adds any Toolbar.Item or subclass
30064      * @param {Roo.Toolbar.Item} item
30065      * @return {Roo.Toolbar.Item} The item
30066      */
30067     addItem : function(item){
30068         var td = this.nextBlock();
30069         item.render(td);
30070         this.items.add(item);
30071         return item;
30072     },
30073     
30074     /**
30075      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30076      * @param {Object/Array} config A button config or array of configs
30077      * @return {Roo.Toolbar.Button/Array}
30078      */
30079     addButton : function(config){
30080         if(config instanceof Array){
30081             var buttons = [];
30082             for(var i = 0, len = config.length; i < len; i++) {
30083                 buttons.push(this.addButton(config[i]));
30084             }
30085             return buttons;
30086         }
30087         var b = config;
30088         if(!(config instanceof Roo.Toolbar.Button)){
30089             b = config.split ?
30090                 new Roo.Toolbar.SplitButton(config) :
30091                 new Roo.Toolbar.Button(config);
30092         }
30093         var td = this.nextBlock();
30094         b.render(td);
30095         this.items.add(b);
30096         return b;
30097     },
30098     
30099     /**
30100      * Adds text to the toolbar
30101      * @param {String} text The text to add
30102      * @return {Roo.Toolbar.Item} The element's item
30103      */
30104     addText : function(text){
30105         return this.addItem(new Roo.Toolbar.TextItem(text));
30106     },
30107     
30108     /**
30109      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30110      * @param {Number} index The index where the item is to be inserted
30111      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30112      * @return {Roo.Toolbar.Button/Item}
30113      */
30114     insertButton : function(index, item){
30115         if(item instanceof Array){
30116             var buttons = [];
30117             for(var i = 0, len = item.length; i < len; i++) {
30118                buttons.push(this.insertButton(index + i, item[i]));
30119             }
30120             return buttons;
30121         }
30122         if (!(item instanceof Roo.Toolbar.Button)){
30123            item = new Roo.Toolbar.Button(item);
30124         }
30125         var td = document.createElement("td");
30126         this.tr.insertBefore(td, this.tr.childNodes[index]);
30127         item.render(td);
30128         this.items.insert(index, item);
30129         return item;
30130     },
30131     
30132     /**
30133      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30134      * @param {Object} config
30135      * @return {Roo.Toolbar.Item} The element's item
30136      */
30137     addDom : function(config, returnEl){
30138         var td = this.nextBlock();
30139         Roo.DomHelper.overwrite(td, config);
30140         var ti = new Roo.Toolbar.Item(td.firstChild);
30141         ti.render(td);
30142         this.items.add(ti);
30143         return ti;
30144     },
30145
30146     /**
30147      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30148      * @type Roo.util.MixedCollection  
30149      */
30150     fields : false,
30151     
30152     /**
30153      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30154      * Note: the field should not have been rendered yet. For a field that has already been
30155      * rendered, use {@link #addElement}.
30156      * @param {Roo.form.Field} field
30157      * @return {Roo.ToolbarItem}
30158      */
30159      
30160       
30161     addField : function(field) {
30162         if (!this.fields) {
30163             var autoId = 0;
30164             this.fields = new Roo.util.MixedCollection(false, function(o){
30165                 return o.id || ("item" + (++autoId));
30166             });
30167
30168         }
30169         
30170         var td = this.nextBlock();
30171         field.render(td);
30172         var ti = new Roo.Toolbar.Item(td.firstChild);
30173         ti.render(td);
30174         this.items.add(ti);
30175         this.fields.add(field);
30176         return ti;
30177     },
30178     /**
30179      * Hide the toolbar
30180      * @method hide
30181      */
30182      
30183       
30184     hide : function()
30185     {
30186         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30187         this.el.child('div').hide();
30188     },
30189     /**
30190      * Show the toolbar
30191      * @method show
30192      */
30193     show : function()
30194     {
30195         this.el.child('div').show();
30196     },
30197       
30198     // private
30199     nextBlock : function(){
30200         var td = document.createElement("td");
30201         this.tr.appendChild(td);
30202         return td;
30203     },
30204
30205     // private
30206     destroy : function(){
30207         if(this.items){ // rendered?
30208             Roo.destroy.apply(Roo, this.items.items);
30209         }
30210         if(this.fields){ // rendered?
30211             Roo.destroy.apply(Roo, this.fields.items);
30212         }
30213         Roo.Element.uncache(this.el, this.tr);
30214     }
30215 };
30216
30217 /**
30218  * @class Roo.Toolbar.Item
30219  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30220  * @constructor
30221  * Creates a new Item
30222  * @param {HTMLElement} el 
30223  */
30224 Roo.Toolbar.Item = function(el){
30225     var cfg = {};
30226     if (typeof (el.xtype) != 'undefined') {
30227         cfg = el;
30228         el = cfg.el;
30229     }
30230     
30231     this.el = Roo.getDom(el);
30232     this.id = Roo.id(this.el);
30233     this.hidden = false;
30234     
30235     this.addEvents({
30236          /**
30237              * @event render
30238              * Fires when the button is rendered
30239              * @param {Button} this
30240              */
30241         'render': true
30242     });
30243     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30244 };
30245 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30246 //Roo.Toolbar.Item.prototype = {
30247     
30248     /**
30249      * Get this item's HTML Element
30250      * @return {HTMLElement}
30251      */
30252     getEl : function(){
30253        return this.el;  
30254     },
30255
30256     // private
30257     render : function(td){
30258         
30259          this.td = td;
30260         td.appendChild(this.el);
30261         
30262         this.fireEvent('render', this);
30263     },
30264     
30265     /**
30266      * Removes and destroys this item.
30267      */
30268     destroy : function(){
30269         this.td.parentNode.removeChild(this.td);
30270     },
30271     
30272     /**
30273      * Shows this item.
30274      */
30275     show: function(){
30276         this.hidden = false;
30277         this.td.style.display = "";
30278     },
30279     
30280     /**
30281      * Hides this item.
30282      */
30283     hide: function(){
30284         this.hidden = true;
30285         this.td.style.display = "none";
30286     },
30287     
30288     /**
30289      * Convenience function for boolean show/hide.
30290      * @param {Boolean} visible true to show/false to hide
30291      */
30292     setVisible: function(visible){
30293         if(visible) {
30294             this.show();
30295         }else{
30296             this.hide();
30297         }
30298     },
30299     
30300     /**
30301      * Try to focus this item.
30302      */
30303     focus : function(){
30304         Roo.fly(this.el).focus();
30305     },
30306     
30307     /**
30308      * Disables this item.
30309      */
30310     disable : function(){
30311         Roo.fly(this.td).addClass("x-item-disabled");
30312         this.disabled = true;
30313         this.el.disabled = true;
30314     },
30315     
30316     /**
30317      * Enables this item.
30318      */
30319     enable : function(){
30320         Roo.fly(this.td).removeClass("x-item-disabled");
30321         this.disabled = false;
30322         this.el.disabled = false;
30323     }
30324 });
30325
30326
30327 /**
30328  * @class Roo.Toolbar.Separator
30329  * @extends Roo.Toolbar.Item
30330  * A simple toolbar separator class
30331  * @constructor
30332  * Creates a new Separator
30333  */
30334 Roo.Toolbar.Separator = function(cfg){
30335     
30336     var s = document.createElement("span");
30337     s.className = "ytb-sep";
30338     if (cfg) {
30339         cfg.el = s;
30340     }
30341     
30342     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30343 };
30344 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30345     enable:Roo.emptyFn,
30346     disable:Roo.emptyFn,
30347     focus:Roo.emptyFn
30348 });
30349
30350 /**
30351  * @class Roo.Toolbar.Spacer
30352  * @extends Roo.Toolbar.Item
30353  * A simple element that adds extra horizontal space to a toolbar.
30354  * @constructor
30355  * Creates a new Spacer
30356  */
30357 Roo.Toolbar.Spacer = function(cfg){
30358     var s = document.createElement("div");
30359     s.className = "ytb-spacer";
30360     if (cfg) {
30361         cfg.el = s;
30362     }
30363     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30364 };
30365 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30366     enable:Roo.emptyFn,
30367     disable:Roo.emptyFn,
30368     focus:Roo.emptyFn
30369 });
30370
30371 /**
30372  * @class Roo.Toolbar.Fill
30373  * @extends Roo.Toolbar.Spacer
30374  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30375  * @constructor
30376  * Creates a new Spacer
30377  */
30378 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30379     // private
30380     render : function(td){
30381         td.style.width = '100%';
30382         Roo.Toolbar.Fill.superclass.render.call(this, td);
30383     }
30384 });
30385
30386 /**
30387  * @class Roo.Toolbar.TextItem
30388  * @extends Roo.Toolbar.Item
30389  * A simple class that renders text directly into a toolbar.
30390  * @constructor
30391  * Creates a new TextItem
30392  * @param {String} text
30393  */
30394 Roo.Toolbar.TextItem = function(cfg){
30395     var  text = cfg || "";
30396     if (typeof(cfg) == 'object') {
30397         text = cfg.text || "";
30398     }  else {
30399         cfg = null;
30400     }
30401     var s = document.createElement("span");
30402     s.className = "ytb-text";
30403     s.innerHTML = text;
30404     if (cfg) {
30405         cfg.el  = s;
30406     }
30407     
30408     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30409 };
30410 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30411     
30412      
30413     enable:Roo.emptyFn,
30414     disable:Roo.emptyFn,
30415     focus:Roo.emptyFn
30416 });
30417
30418 /**
30419  * @class Roo.Toolbar.Button
30420  * @extends Roo.Button
30421  * A button that renders into a toolbar.
30422  * @constructor
30423  * Creates a new Button
30424  * @param {Object} config A standard {@link Roo.Button} config object
30425  */
30426 Roo.Toolbar.Button = function(config){
30427     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30428 };
30429 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30430     render : function(td){
30431         this.td = td;
30432         Roo.Toolbar.Button.superclass.render.call(this, td);
30433     },
30434     
30435     /**
30436      * Removes and destroys this button
30437      */
30438     destroy : function(){
30439         Roo.Toolbar.Button.superclass.destroy.call(this);
30440         this.td.parentNode.removeChild(this.td);
30441     },
30442     
30443     /**
30444      * Shows this button
30445      */
30446     show: function(){
30447         this.hidden = false;
30448         this.td.style.display = "";
30449     },
30450     
30451     /**
30452      * Hides this button
30453      */
30454     hide: function(){
30455         this.hidden = true;
30456         this.td.style.display = "none";
30457     },
30458
30459     /**
30460      * Disables this item
30461      */
30462     disable : function(){
30463         Roo.fly(this.td).addClass("x-item-disabled");
30464         this.disabled = true;
30465     },
30466
30467     /**
30468      * Enables this item
30469      */
30470     enable : function(){
30471         Roo.fly(this.td).removeClass("x-item-disabled");
30472         this.disabled = false;
30473     }
30474 });
30475 // backwards compat
30476 Roo.ToolbarButton = Roo.Toolbar.Button;
30477
30478 /**
30479  * @class Roo.Toolbar.SplitButton
30480  * @extends Roo.SplitButton
30481  * A menu button that renders into a toolbar.
30482  * @constructor
30483  * Creates a new SplitButton
30484  * @param {Object} config A standard {@link Roo.SplitButton} config object
30485  */
30486 Roo.Toolbar.SplitButton = function(config){
30487     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30488 };
30489 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30490     render : function(td){
30491         this.td = td;
30492         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30493     },
30494     
30495     /**
30496      * Removes and destroys this button
30497      */
30498     destroy : function(){
30499         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30500         this.td.parentNode.removeChild(this.td);
30501     },
30502     
30503     /**
30504      * Shows this button
30505      */
30506     show: function(){
30507         this.hidden = false;
30508         this.td.style.display = "";
30509     },
30510     
30511     /**
30512      * Hides this button
30513      */
30514     hide: function(){
30515         this.hidden = true;
30516         this.td.style.display = "none";
30517     }
30518 });
30519
30520 // backwards compat
30521 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30522  * Based on:
30523  * Ext JS Library 1.1.1
30524  * Copyright(c) 2006-2007, Ext JS, LLC.
30525  *
30526  * Originally Released Under LGPL - original licence link has changed is not relivant.
30527  *
30528  * Fork - LGPL
30529  * <script type="text/javascript">
30530  */
30531  
30532 /**
30533  * @class Roo.PagingToolbar
30534  * @extends Roo.Toolbar
30535  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30536  * @constructor
30537  * Create a new PagingToolbar
30538  * @param {Object} config The config object
30539  */
30540 Roo.PagingToolbar = function(el, ds, config)
30541 {
30542     // old args format still supported... - xtype is prefered..
30543     if (typeof(el) == 'object' && el.xtype) {
30544         // created from xtype...
30545         config = el;
30546         ds = el.dataSource;
30547         el = config.container;
30548     }
30549     var items = [];
30550     if (config.items) {
30551         items = config.items;
30552         config.items = [];
30553     }
30554     
30555     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30556     this.ds = ds;
30557     this.cursor = 0;
30558     this.renderButtons(this.el);
30559     this.bind(ds);
30560     
30561     // supprot items array.
30562    
30563     Roo.each(items, function(e) {
30564         this.add(Roo.factory(e));
30565     },this);
30566     
30567 };
30568
30569 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30570     /**
30571      * @cfg {Roo.data.Store} dataSource
30572      * The underlying data store providing the paged data
30573      */
30574     /**
30575      * @cfg {String/HTMLElement/Element} container
30576      * container The id or element that will contain the toolbar
30577      */
30578     /**
30579      * @cfg {Boolean} displayInfo
30580      * True to display the displayMsg (defaults to false)
30581      */
30582     /**
30583      * @cfg {Number} pageSize
30584      * The number of records to display per page (defaults to 20)
30585      */
30586     pageSize: 20,
30587     /**
30588      * @cfg {String} displayMsg
30589      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30590      */
30591     displayMsg : 'Displaying {0} - {1} of {2}',
30592     /**
30593      * @cfg {String} emptyMsg
30594      * The message to display when no records are found (defaults to "No data to display")
30595      */
30596     emptyMsg : 'No data to display',
30597     /**
30598      * Customizable piece of the default paging text (defaults to "Page")
30599      * @type String
30600      */
30601     beforePageText : "Page",
30602     /**
30603      * Customizable piece of the default paging text (defaults to "of %0")
30604      * @type String
30605      */
30606     afterPageText : "of {0}",
30607     /**
30608      * Customizable piece of the default paging text (defaults to "First Page")
30609      * @type String
30610      */
30611     firstText : "First Page",
30612     /**
30613      * Customizable piece of the default paging text (defaults to "Previous Page")
30614      * @type String
30615      */
30616     prevText : "Previous Page",
30617     /**
30618      * Customizable piece of the default paging text (defaults to "Next Page")
30619      * @type String
30620      */
30621     nextText : "Next Page",
30622     /**
30623      * Customizable piece of the default paging text (defaults to "Last Page")
30624      * @type String
30625      */
30626     lastText : "Last Page",
30627     /**
30628      * Customizable piece of the default paging text (defaults to "Refresh")
30629      * @type String
30630      */
30631     refreshText : "Refresh",
30632
30633     // private
30634     renderButtons : function(el){
30635         Roo.PagingToolbar.superclass.render.call(this, el);
30636         this.first = this.addButton({
30637             tooltip: this.firstText,
30638             cls: "x-btn-icon x-grid-page-first",
30639             disabled: true,
30640             handler: this.onClick.createDelegate(this, ["first"])
30641         });
30642         this.prev = this.addButton({
30643             tooltip: this.prevText,
30644             cls: "x-btn-icon x-grid-page-prev",
30645             disabled: true,
30646             handler: this.onClick.createDelegate(this, ["prev"])
30647         });
30648         //this.addSeparator();
30649         this.add(this.beforePageText);
30650         this.field = Roo.get(this.addDom({
30651            tag: "input",
30652            type: "text",
30653            size: "3",
30654            value: "1",
30655            cls: "x-grid-page-number"
30656         }).el);
30657         this.field.on("keydown", this.onPagingKeydown, this);
30658         this.field.on("focus", function(){this.dom.select();});
30659         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30660         this.field.setHeight(18);
30661         //this.addSeparator();
30662         this.next = this.addButton({
30663             tooltip: this.nextText,
30664             cls: "x-btn-icon x-grid-page-next",
30665             disabled: true,
30666             handler: this.onClick.createDelegate(this, ["next"])
30667         });
30668         this.last = this.addButton({
30669             tooltip: this.lastText,
30670             cls: "x-btn-icon x-grid-page-last",
30671             disabled: true,
30672             handler: this.onClick.createDelegate(this, ["last"])
30673         });
30674         //this.addSeparator();
30675         this.loading = this.addButton({
30676             tooltip: this.refreshText,
30677             cls: "x-btn-icon x-grid-loading",
30678             handler: this.onClick.createDelegate(this, ["refresh"])
30679         });
30680
30681         if(this.displayInfo){
30682             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30683         }
30684     },
30685
30686     // private
30687     updateInfo : function(){
30688         if(this.displayEl){
30689             var count = this.ds.getCount();
30690             var msg = count == 0 ?
30691                 this.emptyMsg :
30692                 String.format(
30693                     this.displayMsg,
30694                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30695                 );
30696             this.displayEl.update(msg);
30697         }
30698     },
30699
30700     // private
30701     onLoad : function(ds, r, o){
30702        this.cursor = o.params ? o.params.start : 0;
30703        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30704
30705        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30706        this.field.dom.value = ap;
30707        this.first.setDisabled(ap == 1);
30708        this.prev.setDisabled(ap == 1);
30709        this.next.setDisabled(ap == ps);
30710        this.last.setDisabled(ap == ps);
30711        this.loading.enable();
30712        this.updateInfo();
30713     },
30714
30715     // private
30716     getPageData : function(){
30717         var total = this.ds.getTotalCount();
30718         return {
30719             total : total,
30720             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30721             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30722         };
30723     },
30724
30725     // private
30726     onLoadError : function(){
30727         this.loading.enable();
30728     },
30729
30730     // private
30731     onPagingKeydown : function(e){
30732         var k = e.getKey();
30733         var d = this.getPageData();
30734         if(k == e.RETURN){
30735             var v = this.field.dom.value, pageNum;
30736             if(!v || isNaN(pageNum = parseInt(v, 10))){
30737                 this.field.dom.value = d.activePage;
30738                 return;
30739             }
30740             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30741             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30742             e.stopEvent();
30743         }
30744         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))
30745         {
30746           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30747           this.field.dom.value = pageNum;
30748           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30749           e.stopEvent();
30750         }
30751         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30752         {
30753           var v = this.field.dom.value, pageNum; 
30754           var increment = (e.shiftKey) ? 10 : 1;
30755           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30756             increment *= -1;
30757           }
30758           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30759             this.field.dom.value = d.activePage;
30760             return;
30761           }
30762           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30763           {
30764             this.field.dom.value = parseInt(v, 10) + increment;
30765             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30766             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30767           }
30768           e.stopEvent();
30769         }
30770     },
30771
30772     // private
30773     beforeLoad : function(){
30774         if(this.loading){
30775             this.loading.disable();
30776         }
30777     },
30778
30779     // private
30780     onClick : function(which){
30781         var ds = this.ds;
30782         switch(which){
30783             case "first":
30784                 ds.load({params:{start: 0, limit: this.pageSize}});
30785             break;
30786             case "prev":
30787                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30788             break;
30789             case "next":
30790                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30791             break;
30792             case "last":
30793                 var total = ds.getTotalCount();
30794                 var extra = total % this.pageSize;
30795                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30796                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30797             break;
30798             case "refresh":
30799                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30800             break;
30801         }
30802     },
30803
30804     /**
30805      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30806      * @param {Roo.data.Store} store The data store to unbind
30807      */
30808     unbind : function(ds){
30809         ds.un("beforeload", this.beforeLoad, this);
30810         ds.un("load", this.onLoad, this);
30811         ds.un("loadexception", this.onLoadError, this);
30812         ds.un("remove", this.updateInfo, this);
30813         ds.un("add", this.updateInfo, this);
30814         this.ds = undefined;
30815     },
30816
30817     /**
30818      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30819      * @param {Roo.data.Store} store The data store to bind
30820      */
30821     bind : function(ds){
30822         ds.on("beforeload", this.beforeLoad, this);
30823         ds.on("load", this.onLoad, this);
30824         ds.on("loadexception", this.onLoadError, this);
30825         ds.on("remove", this.updateInfo, this);
30826         ds.on("add", this.updateInfo, this);
30827         this.ds = ds;
30828     }
30829 });/*
30830  * Based on:
30831  * Ext JS Library 1.1.1
30832  * Copyright(c) 2006-2007, Ext JS, LLC.
30833  *
30834  * Originally Released Under LGPL - original licence link has changed is not relivant.
30835  *
30836  * Fork - LGPL
30837  * <script type="text/javascript">
30838  */
30839
30840 /**
30841  * @class Roo.Resizable
30842  * @extends Roo.util.Observable
30843  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30844  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30845  * 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
30846  * the element will be wrapped for you automatically.</p>
30847  * <p>Here is the list of valid resize handles:</p>
30848  * <pre>
30849 Value   Description
30850 ------  -------------------
30851  'n'     north
30852  's'     south
30853  'e'     east
30854  'w'     west
30855  'nw'    northwest
30856  'sw'    southwest
30857  'se'    southeast
30858  'ne'    northeast
30859  'hd'    horizontal drag
30860  'all'   all
30861 </pre>
30862  * <p>Here's an example showing the creation of a typical Resizable:</p>
30863  * <pre><code>
30864 var resizer = new Roo.Resizable("element-id", {
30865     handles: 'all',
30866     minWidth: 200,
30867     minHeight: 100,
30868     maxWidth: 500,
30869     maxHeight: 400,
30870     pinned: true
30871 });
30872 resizer.on("resize", myHandler);
30873 </code></pre>
30874  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30875  * resizer.east.setDisplayed(false);</p>
30876  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30877  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30878  * resize operation's new size (defaults to [0, 0])
30879  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30880  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30881  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30882  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30883  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30884  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30885  * @cfg {Number} width The width of the element in pixels (defaults to null)
30886  * @cfg {Number} height The height of the element in pixels (defaults to null)
30887  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30888  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30889  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30890  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30891  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30892  * in favor of the handles config option (defaults to false)
30893  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30894  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30895  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30896  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30897  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30898  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30899  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30900  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30901  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30902  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30903  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30904  * @constructor
30905  * Create a new resizable component
30906  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30907  * @param {Object} config configuration options
30908   */
30909 Roo.Resizable = function(el, config)
30910 {
30911     this.el = Roo.get(el);
30912
30913     if(config && config.wrap){
30914         config.resizeChild = this.el;
30915         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30916         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30917         this.el.setStyle("overflow", "hidden");
30918         this.el.setPositioning(config.resizeChild.getPositioning());
30919         config.resizeChild.clearPositioning();
30920         if(!config.width || !config.height){
30921             var csize = config.resizeChild.getSize();
30922             this.el.setSize(csize.width, csize.height);
30923         }
30924         if(config.pinned && !config.adjustments){
30925             config.adjustments = "auto";
30926         }
30927     }
30928
30929     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30930     this.proxy.unselectable();
30931     this.proxy.enableDisplayMode('block');
30932
30933     Roo.apply(this, config);
30934
30935     if(this.pinned){
30936         this.disableTrackOver = true;
30937         this.el.addClass("x-resizable-pinned");
30938     }
30939     // if the element isn't positioned, make it relative
30940     var position = this.el.getStyle("position");
30941     if(position != "absolute" && position != "fixed"){
30942         this.el.setStyle("position", "relative");
30943     }
30944     if(!this.handles){ // no handles passed, must be legacy style
30945         this.handles = 's,e,se';
30946         if(this.multiDirectional){
30947             this.handles += ',n,w';
30948         }
30949     }
30950     if(this.handles == "all"){
30951         this.handles = "n s e w ne nw se sw";
30952     }
30953     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30954     var ps = Roo.Resizable.positions;
30955     for(var i = 0, len = hs.length; i < len; i++){
30956         if(hs[i] && ps[hs[i]]){
30957             var pos = ps[hs[i]];
30958             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30959         }
30960     }
30961     // legacy
30962     this.corner = this.southeast;
30963     
30964     // updateBox = the box can move..
30965     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30966         this.updateBox = true;
30967     }
30968
30969     this.activeHandle = null;
30970
30971     if(this.resizeChild){
30972         if(typeof this.resizeChild == "boolean"){
30973             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30974         }else{
30975             this.resizeChild = Roo.get(this.resizeChild, true);
30976         }
30977     }
30978     
30979     if(this.adjustments == "auto"){
30980         var rc = this.resizeChild;
30981         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30982         if(rc && (hw || hn)){
30983             rc.position("relative");
30984             rc.setLeft(hw ? hw.el.getWidth() : 0);
30985             rc.setTop(hn ? hn.el.getHeight() : 0);
30986         }
30987         this.adjustments = [
30988             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30989             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30990         ];
30991     }
30992
30993     if(this.draggable){
30994         this.dd = this.dynamic ?
30995             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30996         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30997     }
30998
30999     // public events
31000     this.addEvents({
31001         /**
31002          * @event beforeresize
31003          * Fired before resize is allowed. Set enabled to false to cancel resize.
31004          * @param {Roo.Resizable} this
31005          * @param {Roo.EventObject} e The mousedown event
31006          */
31007         "beforeresize" : true,
31008         /**
31009          * @event resizing
31010          * Fired a resizing.
31011          * @param {Roo.Resizable} this
31012          * @param {Number} x The new x position
31013          * @param {Number} y The new y position
31014          * @param {Number} w The new w width
31015          * @param {Number} h The new h hight
31016          * @param {Roo.EventObject} e The mouseup event
31017          */
31018         "resizing" : true,
31019         /**
31020          * @event resize
31021          * Fired after a resize.
31022          * @param {Roo.Resizable} this
31023          * @param {Number} width The new width
31024          * @param {Number} height The new height
31025          * @param {Roo.EventObject} e The mouseup event
31026          */
31027         "resize" : true
31028     });
31029
31030     if(this.width !== null && this.height !== null){
31031         this.resizeTo(this.width, this.height);
31032     }else{
31033         this.updateChildSize();
31034     }
31035     if(Roo.isIE){
31036         this.el.dom.style.zoom = 1;
31037     }
31038     Roo.Resizable.superclass.constructor.call(this);
31039 };
31040
31041 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31042         resizeChild : false,
31043         adjustments : [0, 0],
31044         minWidth : 5,
31045         minHeight : 5,
31046         maxWidth : 10000,
31047         maxHeight : 10000,
31048         enabled : true,
31049         animate : false,
31050         duration : .35,
31051         dynamic : false,
31052         handles : false,
31053         multiDirectional : false,
31054         disableTrackOver : false,
31055         easing : 'easeOutStrong',
31056         widthIncrement : 0,
31057         heightIncrement : 0,
31058         pinned : false,
31059         width : null,
31060         height : null,
31061         preserveRatio : false,
31062         transparent: false,
31063         minX: 0,
31064         minY: 0,
31065         draggable: false,
31066
31067         /**
31068          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31069          */
31070         constrainTo: undefined,
31071         /**
31072          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31073          */
31074         resizeRegion: undefined,
31075
31076
31077     /**
31078      * Perform a manual resize
31079      * @param {Number} width
31080      * @param {Number} height
31081      */
31082     resizeTo : function(width, height){
31083         this.el.setSize(width, height);
31084         this.updateChildSize();
31085         this.fireEvent("resize", this, width, height, null);
31086     },
31087
31088     // private
31089     startSizing : function(e, handle){
31090         this.fireEvent("beforeresize", this, e);
31091         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31092
31093             if(!this.overlay){
31094                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31095                 this.overlay.unselectable();
31096                 this.overlay.enableDisplayMode("block");
31097                 this.overlay.on("mousemove", this.onMouseMove, this);
31098                 this.overlay.on("mouseup", this.onMouseUp, this);
31099             }
31100             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31101
31102             this.resizing = true;
31103             this.startBox = this.el.getBox();
31104             this.startPoint = e.getXY();
31105             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31106                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31107
31108             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31109             this.overlay.show();
31110
31111             if(this.constrainTo) {
31112                 var ct = Roo.get(this.constrainTo);
31113                 this.resizeRegion = ct.getRegion().adjust(
31114                     ct.getFrameWidth('t'),
31115                     ct.getFrameWidth('l'),
31116                     -ct.getFrameWidth('b'),
31117                     -ct.getFrameWidth('r')
31118                 );
31119             }
31120
31121             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31122             this.proxy.show();
31123             this.proxy.setBox(this.startBox);
31124             if(!this.dynamic){
31125                 this.proxy.setStyle('visibility', 'visible');
31126             }
31127         }
31128     },
31129
31130     // private
31131     onMouseDown : function(handle, e){
31132         if(this.enabled){
31133             e.stopEvent();
31134             this.activeHandle = handle;
31135             this.startSizing(e, handle);
31136         }
31137     },
31138
31139     // private
31140     onMouseUp : function(e){
31141         var size = this.resizeElement();
31142         this.resizing = false;
31143         this.handleOut();
31144         this.overlay.hide();
31145         this.proxy.hide();
31146         this.fireEvent("resize", this, size.width, size.height, e);
31147     },
31148
31149     // private
31150     updateChildSize : function(){
31151         
31152         if(this.resizeChild){
31153             var el = this.el;
31154             var child = this.resizeChild;
31155             var adj = this.adjustments;
31156             if(el.dom.offsetWidth){
31157                 var b = el.getSize(true);
31158                 child.setSize(b.width+adj[0], b.height+adj[1]);
31159             }
31160             // Second call here for IE
31161             // The first call enables instant resizing and
31162             // the second call corrects scroll bars if they
31163             // exist
31164             if(Roo.isIE){
31165                 setTimeout(function(){
31166                     if(el.dom.offsetWidth){
31167                         var b = el.getSize(true);
31168                         child.setSize(b.width+adj[0], b.height+adj[1]);
31169                     }
31170                 }, 10);
31171             }
31172         }
31173     },
31174
31175     // private
31176     snap : function(value, inc, min){
31177         if(!inc || !value) {
31178             return value;
31179         }
31180         var newValue = value;
31181         var m = value % inc;
31182         if(m > 0){
31183             if(m > (inc/2)){
31184                 newValue = value + (inc-m);
31185             }else{
31186                 newValue = value - m;
31187             }
31188         }
31189         return Math.max(min, newValue);
31190     },
31191
31192     // private
31193     resizeElement : function(){
31194         var box = this.proxy.getBox();
31195         if(this.updateBox){
31196             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31197         }else{
31198             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31199         }
31200         this.updateChildSize();
31201         if(!this.dynamic){
31202             this.proxy.hide();
31203         }
31204         return box;
31205     },
31206
31207     // private
31208     constrain : function(v, diff, m, mx){
31209         if(v - diff < m){
31210             diff = v - m;
31211         }else if(v - diff > mx){
31212             diff = mx - v;
31213         }
31214         return diff;
31215     },
31216
31217     // private
31218     onMouseMove : function(e){
31219         
31220         if(this.enabled){
31221             try{// try catch so if something goes wrong the user doesn't get hung
31222
31223             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31224                 return;
31225             }
31226
31227             //var curXY = this.startPoint;
31228             var curSize = this.curSize || this.startBox;
31229             var x = this.startBox.x, y = this.startBox.y;
31230             var ox = x, oy = y;
31231             var w = curSize.width, h = curSize.height;
31232             var ow = w, oh = h;
31233             var mw = this.minWidth, mh = this.minHeight;
31234             var mxw = this.maxWidth, mxh = this.maxHeight;
31235             var wi = this.widthIncrement;
31236             var hi = this.heightIncrement;
31237
31238             var eventXY = e.getXY();
31239             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31240             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31241
31242             var pos = this.activeHandle.position;
31243
31244             switch(pos){
31245                 case "east":
31246                     w += diffX;
31247                     w = Math.min(Math.max(mw, w), mxw);
31248                     break;
31249              
31250                 case "south":
31251                     h += diffY;
31252                     h = Math.min(Math.max(mh, h), mxh);
31253                     break;
31254                 case "southeast":
31255                     w += diffX;
31256                     h += diffY;
31257                     w = Math.min(Math.max(mw, w), mxw);
31258                     h = Math.min(Math.max(mh, h), mxh);
31259                     break;
31260                 case "north":
31261                     diffY = this.constrain(h, diffY, mh, mxh);
31262                     y += diffY;
31263                     h -= diffY;
31264                     break;
31265                 case "hdrag":
31266                     
31267                     if (wi) {
31268                         var adiffX = Math.abs(diffX);
31269                         var sub = (adiffX % wi); // how much 
31270                         if (sub > (wi/2)) { // far enough to snap
31271                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31272                         } else {
31273                             // remove difference.. 
31274                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31275                         }
31276                     }
31277                     x += diffX;
31278                     x = Math.max(this.minX, x);
31279                     break;
31280                 case "west":
31281                     diffX = this.constrain(w, diffX, mw, mxw);
31282                     x += diffX;
31283                     w -= diffX;
31284                     break;
31285                 case "northeast":
31286                     w += diffX;
31287                     w = Math.min(Math.max(mw, w), mxw);
31288                     diffY = this.constrain(h, diffY, mh, mxh);
31289                     y += diffY;
31290                     h -= diffY;
31291                     break;
31292                 case "northwest":
31293                     diffX = this.constrain(w, diffX, mw, mxw);
31294                     diffY = this.constrain(h, diffY, mh, mxh);
31295                     y += diffY;
31296                     h -= diffY;
31297                     x += diffX;
31298                     w -= diffX;
31299                     break;
31300                case "southwest":
31301                     diffX = this.constrain(w, diffX, mw, mxw);
31302                     h += diffY;
31303                     h = Math.min(Math.max(mh, h), mxh);
31304                     x += diffX;
31305                     w -= diffX;
31306                     break;
31307             }
31308
31309             var sw = this.snap(w, wi, mw);
31310             var sh = this.snap(h, hi, mh);
31311             if(sw != w || sh != h){
31312                 switch(pos){
31313                     case "northeast":
31314                         y -= sh - h;
31315                     break;
31316                     case "north":
31317                         y -= sh - h;
31318                         break;
31319                     case "southwest":
31320                         x -= sw - w;
31321                     break;
31322                     case "west":
31323                         x -= sw - w;
31324                         break;
31325                     case "northwest":
31326                         x -= sw - w;
31327                         y -= sh - h;
31328                     break;
31329                 }
31330                 w = sw;
31331                 h = sh;
31332             }
31333
31334             if(this.preserveRatio){
31335                 switch(pos){
31336                     case "southeast":
31337                     case "east":
31338                         h = oh * (w/ow);
31339                         h = Math.min(Math.max(mh, h), mxh);
31340                         w = ow * (h/oh);
31341                        break;
31342                     case "south":
31343                         w = ow * (h/oh);
31344                         w = Math.min(Math.max(mw, w), mxw);
31345                         h = oh * (w/ow);
31346                         break;
31347                     case "northeast":
31348                         w = ow * (h/oh);
31349                         w = Math.min(Math.max(mw, w), mxw);
31350                         h = oh * (w/ow);
31351                     break;
31352                     case "north":
31353                         var tw = w;
31354                         w = ow * (h/oh);
31355                         w = Math.min(Math.max(mw, w), mxw);
31356                         h = oh * (w/ow);
31357                         x += (tw - w) / 2;
31358                         break;
31359                     case "southwest":
31360                         h = oh * (w/ow);
31361                         h = Math.min(Math.max(mh, h), mxh);
31362                         var tw = w;
31363                         w = ow * (h/oh);
31364                         x += tw - w;
31365                         break;
31366                     case "west":
31367                         var th = h;
31368                         h = oh * (w/ow);
31369                         h = Math.min(Math.max(mh, h), mxh);
31370                         y += (th - h) / 2;
31371                         var tw = w;
31372                         w = ow * (h/oh);
31373                         x += tw - w;
31374                        break;
31375                     case "northwest":
31376                         var tw = w;
31377                         var th = h;
31378                         h = oh * (w/ow);
31379                         h = Math.min(Math.max(mh, h), mxh);
31380                         w = ow * (h/oh);
31381                         y += th - h;
31382                         x += tw - w;
31383                        break;
31384
31385                 }
31386             }
31387             if (pos == 'hdrag') {
31388                 w = ow;
31389             }
31390             this.proxy.setBounds(x, y, w, h);
31391             if(this.dynamic){
31392                 this.resizeElement();
31393             }
31394             }catch(e){}
31395         }
31396         this.fireEvent("resizing", this, x, y, w, h, e);
31397     },
31398
31399     // private
31400     handleOver : function(){
31401         if(this.enabled){
31402             this.el.addClass("x-resizable-over");
31403         }
31404     },
31405
31406     // private
31407     handleOut : function(){
31408         if(!this.resizing){
31409             this.el.removeClass("x-resizable-over");
31410         }
31411     },
31412
31413     /**
31414      * Returns the element this component is bound to.
31415      * @return {Roo.Element}
31416      */
31417     getEl : function(){
31418         return this.el;
31419     },
31420
31421     /**
31422      * Returns the resizeChild element (or null).
31423      * @return {Roo.Element}
31424      */
31425     getResizeChild : function(){
31426         return this.resizeChild;
31427     },
31428     groupHandler : function()
31429     {
31430         
31431     },
31432     /**
31433      * Destroys this resizable. If the element was wrapped and
31434      * removeEl is not true then the element remains.
31435      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31436      */
31437     destroy : function(removeEl){
31438         this.proxy.remove();
31439         if(this.overlay){
31440             this.overlay.removeAllListeners();
31441             this.overlay.remove();
31442         }
31443         var ps = Roo.Resizable.positions;
31444         for(var k in ps){
31445             if(typeof ps[k] != "function" && this[ps[k]]){
31446                 var h = this[ps[k]];
31447                 h.el.removeAllListeners();
31448                 h.el.remove();
31449             }
31450         }
31451         if(removeEl){
31452             this.el.update("");
31453             this.el.remove();
31454         }
31455     }
31456 });
31457
31458 // private
31459 // hash to map config positions to true positions
31460 Roo.Resizable.positions = {
31461     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31462     hd: "hdrag"
31463 };
31464
31465 // private
31466 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31467     if(!this.tpl){
31468         // only initialize the template if resizable is used
31469         var tpl = Roo.DomHelper.createTemplate(
31470             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31471         );
31472         tpl.compile();
31473         Roo.Resizable.Handle.prototype.tpl = tpl;
31474     }
31475     this.position = pos;
31476     this.rz = rz;
31477     // show north drag fro topdra
31478     var handlepos = pos == 'hdrag' ? 'north' : pos;
31479     
31480     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31481     if (pos == 'hdrag') {
31482         this.el.setStyle('cursor', 'pointer');
31483     }
31484     this.el.unselectable();
31485     if(transparent){
31486         this.el.setOpacity(0);
31487     }
31488     this.el.on("mousedown", this.onMouseDown, this);
31489     if(!disableTrackOver){
31490         this.el.on("mouseover", this.onMouseOver, this);
31491         this.el.on("mouseout", this.onMouseOut, this);
31492     }
31493 };
31494
31495 // private
31496 Roo.Resizable.Handle.prototype = {
31497     afterResize : function(rz){
31498         Roo.log('after?');
31499         // do nothing
31500     },
31501     // private
31502     onMouseDown : function(e){
31503         this.rz.onMouseDown(this, e);
31504     },
31505     // private
31506     onMouseOver : function(e){
31507         this.rz.handleOver(this, e);
31508     },
31509     // private
31510     onMouseOut : function(e){
31511         this.rz.handleOut(this, e);
31512     }
31513 };/*
31514  * Based on:
31515  * Ext JS Library 1.1.1
31516  * Copyright(c) 2006-2007, Ext JS, LLC.
31517  *
31518  * Originally Released Under LGPL - original licence link has changed is not relivant.
31519  *
31520  * Fork - LGPL
31521  * <script type="text/javascript">
31522  */
31523
31524 /**
31525  * @class Roo.Editor
31526  * @extends Roo.Component
31527  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31528  * @constructor
31529  * Create a new Editor
31530  * @param {Roo.form.Field} field The Field object (or descendant)
31531  * @param {Object} config The config object
31532  */
31533 Roo.Editor = function(field, config){
31534     Roo.Editor.superclass.constructor.call(this, config);
31535     this.field = field;
31536     this.addEvents({
31537         /**
31538              * @event beforestartedit
31539              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31540              * false from the handler of this event.
31541              * @param {Editor} this
31542              * @param {Roo.Element} boundEl The underlying element bound to this editor
31543              * @param {Mixed} value The field value being set
31544              */
31545         "beforestartedit" : true,
31546         /**
31547              * @event startedit
31548              * Fires when this editor is displayed
31549              * @param {Roo.Element} boundEl The underlying element bound to this editor
31550              * @param {Mixed} value The starting field value
31551              */
31552         "startedit" : true,
31553         /**
31554              * @event beforecomplete
31555              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31556              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31557              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31558              * event will not fire since no edit actually occurred.
31559              * @param {Editor} this
31560              * @param {Mixed} value The current field value
31561              * @param {Mixed} startValue The original field value
31562              */
31563         "beforecomplete" : true,
31564         /**
31565              * @event complete
31566              * Fires after editing is complete and any changed value has been written to the underlying field.
31567              * @param {Editor} this
31568              * @param {Mixed} value The current field value
31569              * @param {Mixed} startValue The original field value
31570              */
31571         "complete" : true,
31572         /**
31573          * @event specialkey
31574          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31575          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31576          * @param {Roo.form.Field} this
31577          * @param {Roo.EventObject} e The event object
31578          */
31579         "specialkey" : true
31580     });
31581 };
31582
31583 Roo.extend(Roo.Editor, Roo.Component, {
31584     /**
31585      * @cfg {Boolean/String} autosize
31586      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31587      * or "height" to adopt the height only (defaults to false)
31588      */
31589     /**
31590      * @cfg {Boolean} revertInvalid
31591      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31592      * validation fails (defaults to true)
31593      */
31594     /**
31595      * @cfg {Boolean} ignoreNoChange
31596      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31597      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31598      * will never be ignored.
31599      */
31600     /**
31601      * @cfg {Boolean} hideEl
31602      * False to keep the bound element visible while the editor is displayed (defaults to true)
31603      */
31604     /**
31605      * @cfg {Mixed} value
31606      * The data value of the underlying field (defaults to "")
31607      */
31608     value : "",
31609     /**
31610      * @cfg {String} alignment
31611      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31612      */
31613     alignment: "c-c?",
31614     /**
31615      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31616      * for bottom-right shadow (defaults to "frame")
31617      */
31618     shadow : "frame",
31619     /**
31620      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31621      */
31622     constrain : false,
31623     /**
31624      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31625      */
31626     completeOnEnter : false,
31627     /**
31628      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31629      */
31630     cancelOnEsc : false,
31631     /**
31632      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31633      */
31634     updateEl : false,
31635
31636     // private
31637     onRender : function(ct, position){
31638         this.el = new Roo.Layer({
31639             shadow: this.shadow,
31640             cls: "x-editor",
31641             parentEl : ct,
31642             shim : this.shim,
31643             shadowOffset:4,
31644             id: this.id,
31645             constrain: this.constrain
31646         });
31647         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31648         if(this.field.msgTarget != 'title'){
31649             this.field.msgTarget = 'qtip';
31650         }
31651         this.field.render(this.el);
31652         if(Roo.isGecko){
31653             this.field.el.dom.setAttribute('autocomplete', 'off');
31654         }
31655         this.field.on("specialkey", this.onSpecialKey, this);
31656         if(this.swallowKeys){
31657             this.field.el.swallowEvent(['keydown','keypress']);
31658         }
31659         this.field.show();
31660         this.field.on("blur", this.onBlur, this);
31661         if(this.field.grow){
31662             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31663         }
31664     },
31665
31666     onSpecialKey : function(field, e)
31667     {
31668         //Roo.log('editor onSpecialKey');
31669         if(this.completeOnEnter && e.getKey() == e.ENTER){
31670             e.stopEvent();
31671             this.completeEdit();
31672             return;
31673         }
31674         // do not fire special key otherwise it might hide close the editor...
31675         if(e.getKey() == e.ENTER){    
31676             return;
31677         }
31678         if(this.cancelOnEsc && e.getKey() == e.ESC){
31679             this.cancelEdit();
31680             return;
31681         } 
31682         this.fireEvent('specialkey', field, e);
31683     
31684     },
31685
31686     /**
31687      * Starts the editing process and shows the editor.
31688      * @param {String/HTMLElement/Element} el The element to edit
31689      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31690       * to the innerHTML of el.
31691      */
31692     startEdit : function(el, value){
31693         if(this.editing){
31694             this.completeEdit();
31695         }
31696         this.boundEl = Roo.get(el);
31697         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31698         if(!this.rendered){
31699             this.render(this.parentEl || document.body);
31700         }
31701         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31702             return;
31703         }
31704         this.startValue = v;
31705         this.field.setValue(v);
31706         if(this.autoSize){
31707             var sz = this.boundEl.getSize();
31708             switch(this.autoSize){
31709                 case "width":
31710                 this.setSize(sz.width,  "");
31711                 break;
31712                 case "height":
31713                 this.setSize("",  sz.height);
31714                 break;
31715                 default:
31716                 this.setSize(sz.width,  sz.height);
31717             }
31718         }
31719         this.el.alignTo(this.boundEl, this.alignment);
31720         this.editing = true;
31721         if(Roo.QuickTips){
31722             Roo.QuickTips.disable();
31723         }
31724         this.show();
31725     },
31726
31727     /**
31728      * Sets the height and width of this editor.
31729      * @param {Number} width The new width
31730      * @param {Number} height The new height
31731      */
31732     setSize : function(w, h){
31733         this.field.setSize(w, h);
31734         if(this.el){
31735             this.el.sync();
31736         }
31737     },
31738
31739     /**
31740      * Realigns the editor to the bound field based on the current alignment config value.
31741      */
31742     realign : function(){
31743         this.el.alignTo(this.boundEl, this.alignment);
31744     },
31745
31746     /**
31747      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31748      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31749      */
31750     completeEdit : function(remainVisible){
31751         if(!this.editing){
31752             return;
31753         }
31754         var v = this.getValue();
31755         if(this.revertInvalid !== false && !this.field.isValid()){
31756             v = this.startValue;
31757             this.cancelEdit(true);
31758         }
31759         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31760             this.editing = false;
31761             this.hide();
31762             return;
31763         }
31764         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31765             this.editing = false;
31766             if(this.updateEl && this.boundEl){
31767                 this.boundEl.update(v);
31768             }
31769             if(remainVisible !== true){
31770                 this.hide();
31771             }
31772             this.fireEvent("complete", this, v, this.startValue);
31773         }
31774     },
31775
31776     // private
31777     onShow : function(){
31778         this.el.show();
31779         if(this.hideEl !== false){
31780             this.boundEl.hide();
31781         }
31782         this.field.show();
31783         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31784             this.fixIEFocus = true;
31785             this.deferredFocus.defer(50, this);
31786         }else{
31787             this.field.focus();
31788         }
31789         this.fireEvent("startedit", this.boundEl, this.startValue);
31790     },
31791
31792     deferredFocus : function(){
31793         if(this.editing){
31794             this.field.focus();
31795         }
31796     },
31797
31798     /**
31799      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31800      * reverted to the original starting value.
31801      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31802      * cancel (defaults to false)
31803      */
31804     cancelEdit : function(remainVisible){
31805         if(this.editing){
31806             this.setValue(this.startValue);
31807             if(remainVisible !== true){
31808                 this.hide();
31809             }
31810         }
31811     },
31812
31813     // private
31814     onBlur : function(){
31815         if(this.allowBlur !== true && this.editing){
31816             this.completeEdit();
31817         }
31818     },
31819
31820     // private
31821     onHide : function(){
31822         if(this.editing){
31823             this.completeEdit();
31824             return;
31825         }
31826         this.field.blur();
31827         if(this.field.collapse){
31828             this.field.collapse();
31829         }
31830         this.el.hide();
31831         if(this.hideEl !== false){
31832             this.boundEl.show();
31833         }
31834         if(Roo.QuickTips){
31835             Roo.QuickTips.enable();
31836         }
31837     },
31838
31839     /**
31840      * Sets the data value of the editor
31841      * @param {Mixed} value Any valid value supported by the underlying field
31842      */
31843     setValue : function(v){
31844         this.field.setValue(v);
31845     },
31846
31847     /**
31848      * Gets the data value of the editor
31849      * @return {Mixed} The data value
31850      */
31851     getValue : function(){
31852         return this.field.getValue();
31853     }
31854 });/*
31855  * Based on:
31856  * Ext JS Library 1.1.1
31857  * Copyright(c) 2006-2007, Ext JS, LLC.
31858  *
31859  * Originally Released Under LGPL - original licence link has changed is not relivant.
31860  *
31861  * Fork - LGPL
31862  * <script type="text/javascript">
31863  */
31864  
31865 /**
31866  * @class Roo.BasicDialog
31867  * @extends Roo.util.Observable
31868  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31869  * <pre><code>
31870 var dlg = new Roo.BasicDialog("my-dlg", {
31871     height: 200,
31872     width: 300,
31873     minHeight: 100,
31874     minWidth: 150,
31875     modal: true,
31876     proxyDrag: true,
31877     shadow: true
31878 });
31879 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31880 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31881 dlg.addButton('Cancel', dlg.hide, dlg);
31882 dlg.show();
31883 </code></pre>
31884   <b>A Dialog should always be a direct child of the body element.</b>
31885  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31886  * @cfg {String} title Default text to display in the title bar (defaults to null)
31887  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31888  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31889  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31890  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31891  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31892  * (defaults to null with no animation)
31893  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31894  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31895  * property for valid values (defaults to 'all')
31896  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31897  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31898  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31899  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31900  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31901  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31902  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31903  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31904  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31905  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31906  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31907  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31908  * draggable = true (defaults to false)
31909  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31910  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31911  * shadow (defaults to false)
31912  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31913  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31914  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31915  * @cfg {Array} buttons Array of buttons
31916  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31917  * @constructor
31918  * Create a new BasicDialog.
31919  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31920  * @param {Object} config Configuration options
31921  */
31922 Roo.BasicDialog = function(el, config){
31923     this.el = Roo.get(el);
31924     var dh = Roo.DomHelper;
31925     if(!this.el && config && config.autoCreate){
31926         if(typeof config.autoCreate == "object"){
31927             if(!config.autoCreate.id){
31928                 config.autoCreate.id = el;
31929             }
31930             this.el = dh.append(document.body,
31931                         config.autoCreate, true);
31932         }else{
31933             this.el = dh.append(document.body,
31934                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31935         }
31936     }
31937     el = this.el;
31938     el.setDisplayed(true);
31939     el.hide = this.hideAction;
31940     this.id = el.id;
31941     el.addClass("x-dlg");
31942
31943     Roo.apply(this, config);
31944
31945     this.proxy = el.createProxy("x-dlg-proxy");
31946     this.proxy.hide = this.hideAction;
31947     this.proxy.setOpacity(.5);
31948     this.proxy.hide();
31949
31950     if(config.width){
31951         el.setWidth(config.width);
31952     }
31953     if(config.height){
31954         el.setHeight(config.height);
31955     }
31956     this.size = el.getSize();
31957     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31958         this.xy = [config.x,config.y];
31959     }else{
31960         this.xy = el.getCenterXY(true);
31961     }
31962     /** The header element @type Roo.Element */
31963     this.header = el.child("> .x-dlg-hd");
31964     /** The body element @type Roo.Element */
31965     this.body = el.child("> .x-dlg-bd");
31966     /** The footer element @type Roo.Element */
31967     this.footer = el.child("> .x-dlg-ft");
31968
31969     if(!this.header){
31970         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31971     }
31972     if(!this.body){
31973         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31974     }
31975
31976     this.header.unselectable();
31977     if(this.title){
31978         this.header.update(this.title);
31979     }
31980     // this element allows the dialog to be focused for keyboard event
31981     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31982     this.focusEl.swallowEvent("click", true);
31983
31984     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31985
31986     // wrap the body and footer for special rendering
31987     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31988     if(this.footer){
31989         this.bwrap.dom.appendChild(this.footer.dom);
31990     }
31991
31992     this.bg = this.el.createChild({
31993         tag: "div", cls:"x-dlg-bg",
31994         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31995     });
31996     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31997
31998
31999     if(this.autoScroll !== false && !this.autoTabs){
32000         this.body.setStyle("overflow", "auto");
32001     }
32002
32003     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32004
32005     if(this.closable !== false){
32006         this.el.addClass("x-dlg-closable");
32007         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32008         this.close.on("click", this.closeClick, this);
32009         this.close.addClassOnOver("x-dlg-close-over");
32010     }
32011     if(this.collapsible !== false){
32012         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32013         this.collapseBtn.on("click", this.collapseClick, this);
32014         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32015         this.header.on("dblclick", this.collapseClick, this);
32016     }
32017     if(this.resizable !== false){
32018         this.el.addClass("x-dlg-resizable");
32019         this.resizer = new Roo.Resizable(el, {
32020             minWidth: this.minWidth || 80,
32021             minHeight:this.minHeight || 80,
32022             handles: this.resizeHandles || "all",
32023             pinned: true
32024         });
32025         this.resizer.on("beforeresize", this.beforeResize, this);
32026         this.resizer.on("resize", this.onResize, this);
32027     }
32028     if(this.draggable !== false){
32029         el.addClass("x-dlg-draggable");
32030         if (!this.proxyDrag) {
32031             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32032         }
32033         else {
32034             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32035         }
32036         dd.setHandleElId(this.header.id);
32037         dd.endDrag = this.endMove.createDelegate(this);
32038         dd.startDrag = this.startMove.createDelegate(this);
32039         dd.onDrag = this.onDrag.createDelegate(this);
32040         dd.scroll = false;
32041         this.dd = dd;
32042     }
32043     if(this.modal){
32044         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32045         this.mask.enableDisplayMode("block");
32046         this.mask.hide();
32047         this.el.addClass("x-dlg-modal");
32048     }
32049     if(this.shadow){
32050         this.shadow = new Roo.Shadow({
32051             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32052             offset : this.shadowOffset
32053         });
32054     }else{
32055         this.shadowOffset = 0;
32056     }
32057     if(Roo.useShims && this.shim !== false){
32058         this.shim = this.el.createShim();
32059         this.shim.hide = this.hideAction;
32060         this.shim.hide();
32061     }else{
32062         this.shim = false;
32063     }
32064     if(this.autoTabs){
32065         this.initTabs();
32066     }
32067     if (this.buttons) { 
32068         var bts= this.buttons;
32069         this.buttons = [];
32070         Roo.each(bts, function(b) {
32071             this.addButton(b);
32072         }, this);
32073     }
32074     
32075     
32076     this.addEvents({
32077         /**
32078          * @event keydown
32079          * Fires when a key is pressed
32080          * @param {Roo.BasicDialog} this
32081          * @param {Roo.EventObject} e
32082          */
32083         "keydown" : true,
32084         /**
32085          * @event move
32086          * Fires when this dialog is moved by the user.
32087          * @param {Roo.BasicDialog} this
32088          * @param {Number} x The new page X
32089          * @param {Number} y The new page Y
32090          */
32091         "move" : true,
32092         /**
32093          * @event resize
32094          * Fires when this dialog is resized by the user.
32095          * @param {Roo.BasicDialog} this
32096          * @param {Number} width The new width
32097          * @param {Number} height The new height
32098          */
32099         "resize" : true,
32100         /**
32101          * @event beforehide
32102          * Fires before this dialog is hidden.
32103          * @param {Roo.BasicDialog} this
32104          */
32105         "beforehide" : true,
32106         /**
32107          * @event hide
32108          * Fires when this dialog is hidden.
32109          * @param {Roo.BasicDialog} this
32110          */
32111         "hide" : true,
32112         /**
32113          * @event beforeshow
32114          * Fires before this dialog is shown.
32115          * @param {Roo.BasicDialog} this
32116          */
32117         "beforeshow" : true,
32118         /**
32119          * @event show
32120          * Fires when this dialog is shown.
32121          * @param {Roo.BasicDialog} this
32122          */
32123         "show" : true
32124     });
32125     el.on("keydown", this.onKeyDown, this);
32126     el.on("mousedown", this.toFront, this);
32127     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32128     this.el.hide();
32129     Roo.DialogManager.register(this);
32130     Roo.BasicDialog.superclass.constructor.call(this);
32131 };
32132
32133 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32134     shadowOffset: Roo.isIE ? 6 : 5,
32135     minHeight: 80,
32136     minWidth: 200,
32137     minButtonWidth: 75,
32138     defaultButton: null,
32139     buttonAlign: "right",
32140     tabTag: 'div',
32141     firstShow: true,
32142
32143     /**
32144      * Sets the dialog title text
32145      * @param {String} text The title text to display
32146      * @return {Roo.BasicDialog} this
32147      */
32148     setTitle : function(text){
32149         this.header.update(text);
32150         return this;
32151     },
32152
32153     // private
32154     closeClick : function(){
32155         this.hide();
32156     },
32157
32158     // private
32159     collapseClick : function(){
32160         this[this.collapsed ? "expand" : "collapse"]();
32161     },
32162
32163     /**
32164      * Collapses the dialog to its minimized state (only the title bar is visible).
32165      * Equivalent to the user clicking the collapse dialog button.
32166      */
32167     collapse : function(){
32168         if(!this.collapsed){
32169             this.collapsed = true;
32170             this.el.addClass("x-dlg-collapsed");
32171             this.restoreHeight = this.el.getHeight();
32172             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32173         }
32174     },
32175
32176     /**
32177      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32178      * clicking the expand dialog button.
32179      */
32180     expand : function(){
32181         if(this.collapsed){
32182             this.collapsed = false;
32183             this.el.removeClass("x-dlg-collapsed");
32184             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32185         }
32186     },
32187
32188     /**
32189      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32190      * @return {Roo.TabPanel} The tabs component
32191      */
32192     initTabs : function(){
32193         var tabs = this.getTabs();
32194         while(tabs.getTab(0)){
32195             tabs.removeTab(0);
32196         }
32197         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32198             var dom = el.dom;
32199             tabs.addTab(Roo.id(dom), dom.title);
32200             dom.title = "";
32201         });
32202         tabs.activate(0);
32203         return tabs;
32204     },
32205
32206     // private
32207     beforeResize : function(){
32208         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32209     },
32210
32211     // private
32212     onResize : function(){
32213         this.refreshSize();
32214         this.syncBodyHeight();
32215         this.adjustAssets();
32216         this.focus();
32217         this.fireEvent("resize", this, this.size.width, this.size.height);
32218     },
32219
32220     // private
32221     onKeyDown : function(e){
32222         if(this.isVisible()){
32223             this.fireEvent("keydown", this, e);
32224         }
32225     },
32226
32227     /**
32228      * Resizes the dialog.
32229      * @param {Number} width
32230      * @param {Number} height
32231      * @return {Roo.BasicDialog} this
32232      */
32233     resizeTo : function(width, height){
32234         this.el.setSize(width, height);
32235         this.size = {width: width, height: height};
32236         this.syncBodyHeight();
32237         if(this.fixedcenter){
32238             this.center();
32239         }
32240         if(this.isVisible()){
32241             this.constrainXY();
32242             this.adjustAssets();
32243         }
32244         this.fireEvent("resize", this, width, height);
32245         return this;
32246     },
32247
32248
32249     /**
32250      * Resizes the dialog to fit the specified content size.
32251      * @param {Number} width
32252      * @param {Number} height
32253      * @return {Roo.BasicDialog} this
32254      */
32255     setContentSize : function(w, h){
32256         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32257         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32258         //if(!this.el.isBorderBox()){
32259             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32260             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32261         //}
32262         if(this.tabs){
32263             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32264             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32265         }
32266         this.resizeTo(w, h);
32267         return this;
32268     },
32269
32270     /**
32271      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32272      * executed in response to a particular key being pressed while the dialog is active.
32273      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32274      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32275      * @param {Function} fn The function to call
32276      * @param {Object} scope (optional) The scope of the function
32277      * @return {Roo.BasicDialog} this
32278      */
32279     addKeyListener : function(key, fn, scope){
32280         var keyCode, shift, ctrl, alt;
32281         if(typeof key == "object" && !(key instanceof Array)){
32282             keyCode = key["key"];
32283             shift = key["shift"];
32284             ctrl = key["ctrl"];
32285             alt = key["alt"];
32286         }else{
32287             keyCode = key;
32288         }
32289         var handler = function(dlg, e){
32290             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32291                 var k = e.getKey();
32292                 if(keyCode instanceof Array){
32293                     for(var i = 0, len = keyCode.length; i < len; i++){
32294                         if(keyCode[i] == k){
32295                           fn.call(scope || window, dlg, k, e);
32296                           return;
32297                         }
32298                     }
32299                 }else{
32300                     if(k == keyCode){
32301                         fn.call(scope || window, dlg, k, e);
32302                     }
32303                 }
32304             }
32305         };
32306         this.on("keydown", handler);
32307         return this;
32308     },
32309
32310     /**
32311      * Returns the TabPanel component (creates it if it doesn't exist).
32312      * Note: If you wish to simply check for the existence of tabs without creating them,
32313      * check for a null 'tabs' property.
32314      * @return {Roo.TabPanel} The tabs component
32315      */
32316     getTabs : function(){
32317         if(!this.tabs){
32318             this.el.addClass("x-dlg-auto-tabs");
32319             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32320             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32321         }
32322         return this.tabs;
32323     },
32324
32325     /**
32326      * Adds a button to the footer section of the dialog.
32327      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32328      * object or a valid Roo.DomHelper element config
32329      * @param {Function} handler The function called when the button is clicked
32330      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32331      * @return {Roo.Button} The new button
32332      */
32333     addButton : function(config, handler, scope){
32334         var dh = Roo.DomHelper;
32335         if(!this.footer){
32336             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32337         }
32338         if(!this.btnContainer){
32339             var tb = this.footer.createChild({
32340
32341                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32342                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32343             }, null, true);
32344             this.btnContainer = tb.firstChild.firstChild.firstChild;
32345         }
32346         var bconfig = {
32347             handler: handler,
32348             scope: scope,
32349             minWidth: this.minButtonWidth,
32350             hideParent:true
32351         };
32352         if(typeof config == "string"){
32353             bconfig.text = config;
32354         }else{
32355             if(config.tag){
32356                 bconfig.dhconfig = config;
32357             }else{
32358                 Roo.apply(bconfig, config);
32359             }
32360         }
32361         var fc = false;
32362         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32363             bconfig.position = Math.max(0, bconfig.position);
32364             fc = this.btnContainer.childNodes[bconfig.position];
32365         }
32366          
32367         var btn = new Roo.Button(
32368             fc ? 
32369                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32370                 : this.btnContainer.appendChild(document.createElement("td")),
32371             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32372             bconfig
32373         );
32374         this.syncBodyHeight();
32375         if(!this.buttons){
32376             /**
32377              * Array of all the buttons that have been added to this dialog via addButton
32378              * @type Array
32379              */
32380             this.buttons = [];
32381         }
32382         this.buttons.push(btn);
32383         return btn;
32384     },
32385
32386     /**
32387      * Sets the default button to be focused when the dialog is displayed.
32388      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32389      * @return {Roo.BasicDialog} this
32390      */
32391     setDefaultButton : function(btn){
32392         this.defaultButton = btn;
32393         return this;
32394     },
32395
32396     // private
32397     getHeaderFooterHeight : function(safe){
32398         var height = 0;
32399         if(this.header){
32400            height += this.header.getHeight();
32401         }
32402         if(this.footer){
32403            var fm = this.footer.getMargins();
32404             height += (this.footer.getHeight()+fm.top+fm.bottom);
32405         }
32406         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32407         height += this.centerBg.getPadding("tb");
32408         return height;
32409     },
32410
32411     // private
32412     syncBodyHeight : function()
32413     {
32414         var bd = this.body, // the text
32415             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32416             bw = this.bwrap;
32417         var height = this.size.height - this.getHeaderFooterHeight(false);
32418         bd.setHeight(height-bd.getMargins("tb"));
32419         var hh = this.header.getHeight();
32420         var h = this.size.height-hh;
32421         cb.setHeight(h);
32422         
32423         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32424         bw.setHeight(h-cb.getPadding("tb"));
32425         
32426         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32427         bd.setWidth(bw.getWidth(true));
32428         if(this.tabs){
32429             this.tabs.syncHeight();
32430             if(Roo.isIE){
32431                 this.tabs.el.repaint();
32432             }
32433         }
32434     },
32435
32436     /**
32437      * Restores the previous state of the dialog if Roo.state is configured.
32438      * @return {Roo.BasicDialog} this
32439      */
32440     restoreState : function(){
32441         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32442         if(box && box.width){
32443             this.xy = [box.x, box.y];
32444             this.resizeTo(box.width, box.height);
32445         }
32446         return this;
32447     },
32448
32449     // private
32450     beforeShow : function(){
32451         this.expand();
32452         if(this.fixedcenter){
32453             this.xy = this.el.getCenterXY(true);
32454         }
32455         if(this.modal){
32456             Roo.get(document.body).addClass("x-body-masked");
32457             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32458             this.mask.show();
32459         }
32460         this.constrainXY();
32461     },
32462
32463     // private
32464     animShow : function(){
32465         var b = Roo.get(this.animateTarget).getBox();
32466         this.proxy.setSize(b.width, b.height);
32467         this.proxy.setLocation(b.x, b.y);
32468         this.proxy.show();
32469         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32470                     true, .35, this.showEl.createDelegate(this));
32471     },
32472
32473     /**
32474      * Shows the dialog.
32475      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32476      * @return {Roo.BasicDialog} this
32477      */
32478     show : function(animateTarget){
32479         if (this.fireEvent("beforeshow", this) === false){
32480             return;
32481         }
32482         if(this.syncHeightBeforeShow){
32483             this.syncBodyHeight();
32484         }else if(this.firstShow){
32485             this.firstShow = false;
32486             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32487         }
32488         this.animateTarget = animateTarget || this.animateTarget;
32489         if(!this.el.isVisible()){
32490             this.beforeShow();
32491             if(this.animateTarget && Roo.get(this.animateTarget)){
32492                 this.animShow();
32493             }else{
32494                 this.showEl();
32495             }
32496         }
32497         return this;
32498     },
32499
32500     // private
32501     showEl : function(){
32502         this.proxy.hide();
32503         this.el.setXY(this.xy);
32504         this.el.show();
32505         this.adjustAssets(true);
32506         this.toFront();
32507         this.focus();
32508         // IE peekaboo bug - fix found by Dave Fenwick
32509         if(Roo.isIE){
32510             this.el.repaint();
32511         }
32512         this.fireEvent("show", this);
32513     },
32514
32515     /**
32516      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32517      * dialog itself will receive focus.
32518      */
32519     focus : function(){
32520         if(this.defaultButton){
32521             this.defaultButton.focus();
32522         }else{
32523             this.focusEl.focus();
32524         }
32525     },
32526
32527     // private
32528     constrainXY : function(){
32529         if(this.constraintoviewport !== false){
32530             if(!this.viewSize){
32531                 if(this.container){
32532                     var s = this.container.getSize();
32533                     this.viewSize = [s.width, s.height];
32534                 }else{
32535                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32536                 }
32537             }
32538             var s = Roo.get(this.container||document).getScroll();
32539
32540             var x = this.xy[0], y = this.xy[1];
32541             var w = this.size.width, h = this.size.height;
32542             var vw = this.viewSize[0], vh = this.viewSize[1];
32543             // only move it if it needs it
32544             var moved = false;
32545             // first validate right/bottom
32546             if(x + w > vw+s.left){
32547                 x = vw - w;
32548                 moved = true;
32549             }
32550             if(y + h > vh+s.top){
32551                 y = vh - h;
32552                 moved = true;
32553             }
32554             // then make sure top/left isn't negative
32555             if(x < s.left){
32556                 x = s.left;
32557                 moved = true;
32558             }
32559             if(y < s.top){
32560                 y = s.top;
32561                 moved = true;
32562             }
32563             if(moved){
32564                 // cache xy
32565                 this.xy = [x, y];
32566                 if(this.isVisible()){
32567                     this.el.setLocation(x, y);
32568                     this.adjustAssets();
32569                 }
32570             }
32571         }
32572     },
32573
32574     // private
32575     onDrag : function(){
32576         if(!this.proxyDrag){
32577             this.xy = this.el.getXY();
32578             this.adjustAssets();
32579         }
32580     },
32581
32582     // private
32583     adjustAssets : function(doShow){
32584         var x = this.xy[0], y = this.xy[1];
32585         var w = this.size.width, h = this.size.height;
32586         if(doShow === true){
32587             if(this.shadow){
32588                 this.shadow.show(this.el);
32589             }
32590             if(this.shim){
32591                 this.shim.show();
32592             }
32593         }
32594         if(this.shadow && this.shadow.isVisible()){
32595             this.shadow.show(this.el);
32596         }
32597         if(this.shim && this.shim.isVisible()){
32598             this.shim.setBounds(x, y, w, h);
32599         }
32600     },
32601
32602     // private
32603     adjustViewport : function(w, h){
32604         if(!w || !h){
32605             w = Roo.lib.Dom.getViewWidth();
32606             h = Roo.lib.Dom.getViewHeight();
32607         }
32608         // cache the size
32609         this.viewSize = [w, h];
32610         if(this.modal && this.mask.isVisible()){
32611             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32612             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32613         }
32614         if(this.isVisible()){
32615             this.constrainXY();
32616         }
32617     },
32618
32619     /**
32620      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32621      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32622      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32623      */
32624     destroy : function(removeEl){
32625         if(this.isVisible()){
32626             this.animateTarget = null;
32627             this.hide();
32628         }
32629         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32630         if(this.tabs){
32631             this.tabs.destroy(removeEl);
32632         }
32633         Roo.destroy(
32634              this.shim,
32635              this.proxy,
32636              this.resizer,
32637              this.close,
32638              this.mask
32639         );
32640         if(this.dd){
32641             this.dd.unreg();
32642         }
32643         if(this.buttons){
32644            for(var i = 0, len = this.buttons.length; i < len; i++){
32645                this.buttons[i].destroy();
32646            }
32647         }
32648         this.el.removeAllListeners();
32649         if(removeEl === true){
32650             this.el.update("");
32651             this.el.remove();
32652         }
32653         Roo.DialogManager.unregister(this);
32654     },
32655
32656     // private
32657     startMove : function(){
32658         if(this.proxyDrag){
32659             this.proxy.show();
32660         }
32661         if(this.constraintoviewport !== false){
32662             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32663         }
32664     },
32665
32666     // private
32667     endMove : function(){
32668         if(!this.proxyDrag){
32669             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32670         }else{
32671             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32672             this.proxy.hide();
32673         }
32674         this.refreshSize();
32675         this.adjustAssets();
32676         this.focus();
32677         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32678     },
32679
32680     /**
32681      * Brings this dialog to the front of any other visible dialogs
32682      * @return {Roo.BasicDialog} this
32683      */
32684     toFront : function(){
32685         Roo.DialogManager.bringToFront(this);
32686         return this;
32687     },
32688
32689     /**
32690      * Sends this dialog to the back (under) of any other visible dialogs
32691      * @return {Roo.BasicDialog} this
32692      */
32693     toBack : function(){
32694         Roo.DialogManager.sendToBack(this);
32695         return this;
32696     },
32697
32698     /**
32699      * Centers this dialog in the viewport
32700      * @return {Roo.BasicDialog} this
32701      */
32702     center : function(){
32703         var xy = this.el.getCenterXY(true);
32704         this.moveTo(xy[0], xy[1]);
32705         return this;
32706     },
32707
32708     /**
32709      * Moves the dialog's top-left corner to the specified point
32710      * @param {Number} x
32711      * @param {Number} y
32712      * @return {Roo.BasicDialog} this
32713      */
32714     moveTo : function(x, y){
32715         this.xy = [x,y];
32716         if(this.isVisible()){
32717             this.el.setXY(this.xy);
32718             this.adjustAssets();
32719         }
32720         return this;
32721     },
32722
32723     /**
32724      * Aligns the dialog to the specified element
32725      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32726      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32727      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32728      * @return {Roo.BasicDialog} this
32729      */
32730     alignTo : function(element, position, offsets){
32731         this.xy = this.el.getAlignToXY(element, position, offsets);
32732         if(this.isVisible()){
32733             this.el.setXY(this.xy);
32734             this.adjustAssets();
32735         }
32736         return this;
32737     },
32738
32739     /**
32740      * Anchors an element to another element and realigns it when the window is resized.
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      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32745      * is a number, it is used as the buffer delay (defaults to 50ms).
32746      * @return {Roo.BasicDialog} this
32747      */
32748     anchorTo : function(el, alignment, offsets, monitorScroll){
32749         var action = function(){
32750             this.alignTo(el, alignment, offsets);
32751         };
32752         Roo.EventManager.onWindowResize(action, this);
32753         var tm = typeof monitorScroll;
32754         if(tm != 'undefined'){
32755             Roo.EventManager.on(window, 'scroll', action, this,
32756                 {buffer: tm == 'number' ? monitorScroll : 50});
32757         }
32758         action.call(this);
32759         return this;
32760     },
32761
32762     /**
32763      * Returns true if the dialog is visible
32764      * @return {Boolean}
32765      */
32766     isVisible : function(){
32767         return this.el.isVisible();
32768     },
32769
32770     // private
32771     animHide : function(callback){
32772         var b = Roo.get(this.animateTarget).getBox();
32773         this.proxy.show();
32774         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32775         this.el.hide();
32776         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32777                     this.hideEl.createDelegate(this, [callback]));
32778     },
32779
32780     /**
32781      * Hides the dialog.
32782      * @param {Function} callback (optional) Function to call when the dialog is hidden
32783      * @return {Roo.BasicDialog} this
32784      */
32785     hide : function(callback){
32786         if (this.fireEvent("beforehide", this) === false){
32787             return;
32788         }
32789         if(this.shadow){
32790             this.shadow.hide();
32791         }
32792         if(this.shim) {
32793           this.shim.hide();
32794         }
32795         // sometimes animateTarget seems to get set.. causing problems...
32796         // this just double checks..
32797         if(this.animateTarget && Roo.get(this.animateTarget)) {
32798            this.animHide(callback);
32799         }else{
32800             this.el.hide();
32801             this.hideEl(callback);
32802         }
32803         return this;
32804     },
32805
32806     // private
32807     hideEl : function(callback){
32808         this.proxy.hide();
32809         if(this.modal){
32810             this.mask.hide();
32811             Roo.get(document.body).removeClass("x-body-masked");
32812         }
32813         this.fireEvent("hide", this);
32814         if(typeof callback == "function"){
32815             callback();
32816         }
32817     },
32818
32819     // private
32820     hideAction : function(){
32821         this.setLeft("-10000px");
32822         this.setTop("-10000px");
32823         this.setStyle("visibility", "hidden");
32824     },
32825
32826     // private
32827     refreshSize : function(){
32828         this.size = this.el.getSize();
32829         this.xy = this.el.getXY();
32830         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32831     },
32832
32833     // private
32834     // z-index is managed by the DialogManager and may be overwritten at any time
32835     setZIndex : function(index){
32836         if(this.modal){
32837             this.mask.setStyle("z-index", index);
32838         }
32839         if(this.shim){
32840             this.shim.setStyle("z-index", ++index);
32841         }
32842         if(this.shadow){
32843             this.shadow.setZIndex(++index);
32844         }
32845         this.el.setStyle("z-index", ++index);
32846         if(this.proxy){
32847             this.proxy.setStyle("z-index", ++index);
32848         }
32849         if(this.resizer){
32850             this.resizer.proxy.setStyle("z-index", ++index);
32851         }
32852
32853         this.lastZIndex = index;
32854     },
32855
32856     /**
32857      * Returns the element for this dialog
32858      * @return {Roo.Element} The underlying dialog Element
32859      */
32860     getEl : function(){
32861         return this.el;
32862     }
32863 });
32864
32865 /**
32866  * @class Roo.DialogManager
32867  * Provides global access to BasicDialogs that have been created and
32868  * support for z-indexing (layering) multiple open dialogs.
32869  */
32870 Roo.DialogManager = function(){
32871     var list = {};
32872     var accessList = [];
32873     var front = null;
32874
32875     // private
32876     var sortDialogs = function(d1, d2){
32877         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32878     };
32879
32880     // private
32881     var orderDialogs = function(){
32882         accessList.sort(sortDialogs);
32883         var seed = Roo.DialogManager.zseed;
32884         for(var i = 0, len = accessList.length; i < len; i++){
32885             var dlg = accessList[i];
32886             if(dlg){
32887                 dlg.setZIndex(seed + (i*10));
32888             }
32889         }
32890     };
32891
32892     return {
32893         /**
32894          * The starting z-index for BasicDialogs (defaults to 9000)
32895          * @type Number The z-index value
32896          */
32897         zseed : 9000,
32898
32899         // private
32900         register : function(dlg){
32901             list[dlg.id] = dlg;
32902             accessList.push(dlg);
32903         },
32904
32905         // private
32906         unregister : function(dlg){
32907             delete list[dlg.id];
32908             var i=0;
32909             var len=0;
32910             if(!accessList.indexOf){
32911                 for(  i = 0, len = accessList.length; i < len; i++){
32912                     if(accessList[i] == dlg){
32913                         accessList.splice(i, 1);
32914                         return;
32915                     }
32916                 }
32917             }else{
32918                  i = accessList.indexOf(dlg);
32919                 if(i != -1){
32920                     accessList.splice(i, 1);
32921                 }
32922             }
32923         },
32924
32925         /**
32926          * Gets a registered dialog by id
32927          * @param {String/Object} id The id of the dialog or a dialog
32928          * @return {Roo.BasicDialog} this
32929          */
32930         get : function(id){
32931             return typeof id == "object" ? id : list[id];
32932         },
32933
32934         /**
32935          * Brings the specified dialog to the front
32936          * @param {String/Object} dlg The id of the dialog or a dialog
32937          * @return {Roo.BasicDialog} this
32938          */
32939         bringToFront : function(dlg){
32940             dlg = this.get(dlg);
32941             if(dlg != front){
32942                 front = dlg;
32943                 dlg._lastAccess = new Date().getTime();
32944                 orderDialogs();
32945             }
32946             return dlg;
32947         },
32948
32949         /**
32950          * Sends the specified dialog to the back
32951          * @param {String/Object} dlg The id of the dialog or a dialog
32952          * @return {Roo.BasicDialog} this
32953          */
32954         sendToBack : function(dlg){
32955             dlg = this.get(dlg);
32956             dlg._lastAccess = -(new Date().getTime());
32957             orderDialogs();
32958             return dlg;
32959         },
32960
32961         /**
32962          * Hides all dialogs
32963          */
32964         hideAll : function(){
32965             for(var id in list){
32966                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32967                     list[id].hide();
32968                 }
32969             }
32970         }
32971     };
32972 }();
32973
32974 /**
32975  * @class Roo.LayoutDialog
32976  * @extends Roo.BasicDialog
32977  * Dialog which provides adjustments for working with a layout in a Dialog.
32978  * Add your necessary layout config options to the dialog's config.<br>
32979  * Example usage (including a nested layout):
32980  * <pre><code>
32981 if(!dialog){
32982     dialog = new Roo.LayoutDialog("download-dlg", {
32983         modal: true,
32984         width:600,
32985         height:450,
32986         shadow:true,
32987         minWidth:500,
32988         minHeight:350,
32989         autoTabs:true,
32990         proxyDrag:true,
32991         // layout config merges with the dialog config
32992         center:{
32993             tabPosition: "top",
32994             alwaysShowTabs: true
32995         }
32996     });
32997     dialog.addKeyListener(27, dialog.hide, dialog);
32998     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32999     dialog.addButton("Build It!", this.getDownload, this);
33000
33001     // we can even add nested layouts
33002     var innerLayout = new Roo.BorderLayout("dl-inner", {
33003         east: {
33004             initialSize: 200,
33005             autoScroll:true,
33006             split:true
33007         },
33008         center: {
33009             autoScroll:true
33010         }
33011     });
33012     innerLayout.beginUpdate();
33013     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33014     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33015     innerLayout.endUpdate(true);
33016
33017     var layout = dialog.getLayout();
33018     layout.beginUpdate();
33019     layout.add("center", new Roo.ContentPanel("standard-panel",
33020                         {title: "Download the Source", fitToFrame:true}));
33021     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33022                {title: "Build your own roo.js"}));
33023     layout.getRegion("center").showPanel(sp);
33024     layout.endUpdate();
33025 }
33026 </code></pre>
33027     * @constructor
33028     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33029     * @param {Object} config configuration options
33030   */
33031 Roo.LayoutDialog = function(el, cfg){
33032     
33033     var config=  cfg;
33034     if (typeof(cfg) == 'undefined') {
33035         config = Roo.apply({}, el);
33036         // not sure why we use documentElement here.. - it should always be body.
33037         // IE7 borks horribly if we use documentElement.
33038         // webkit also does not like documentElement - it creates a body element...
33039         el = Roo.get( document.body || document.documentElement ).createChild();
33040         //config.autoCreate = true;
33041     }
33042     
33043     
33044     config.autoTabs = false;
33045     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33046     this.body.setStyle({overflow:"hidden", position:"relative"});
33047     this.layout = new Roo.BorderLayout(this.body.dom, config);
33048     this.layout.monitorWindowResize = false;
33049     this.el.addClass("x-dlg-auto-layout");
33050     // fix case when center region overwrites center function
33051     this.center = Roo.BasicDialog.prototype.center;
33052     this.on("show", this.layout.layout, this.layout, true);
33053     if (config.items) {
33054         var xitems = config.items;
33055         delete config.items;
33056         Roo.each(xitems, this.addxtype, this);
33057     }
33058     
33059     
33060 };
33061 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33062     /**
33063      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33064      * @deprecated
33065      */
33066     endUpdate : function(){
33067         this.layout.endUpdate();
33068     },
33069
33070     /**
33071      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33072      *  @deprecated
33073      */
33074     beginUpdate : function(){
33075         this.layout.beginUpdate();
33076     },
33077
33078     /**
33079      * Get the BorderLayout for this dialog
33080      * @return {Roo.BorderLayout}
33081      */
33082     getLayout : function(){
33083         return this.layout;
33084     },
33085
33086     showEl : function(){
33087         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33088         if(Roo.isIE7){
33089             this.layout.layout();
33090         }
33091     },
33092
33093     // private
33094     // Use the syncHeightBeforeShow config option to control this automatically
33095     syncBodyHeight : function(){
33096         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33097         if(this.layout){this.layout.layout();}
33098     },
33099     
33100       /**
33101      * Add an xtype element (actually adds to the layout.)
33102      * @return {Object} xdata xtype object data.
33103      */
33104     
33105     addxtype : function(c) {
33106         return this.layout.addxtype(c);
33107     }
33108 });/*
33109  * Based on:
33110  * Ext JS Library 1.1.1
33111  * Copyright(c) 2006-2007, Ext JS, LLC.
33112  *
33113  * Originally Released Under LGPL - original licence link has changed is not relivant.
33114  *
33115  * Fork - LGPL
33116  * <script type="text/javascript">
33117  */
33118  
33119 /**
33120  * @class Roo.MessageBox
33121  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33122  * Example usage:
33123  *<pre><code>
33124 // Basic alert:
33125 Roo.Msg.alert('Status', 'Changes saved successfully.');
33126
33127 // Prompt for user data:
33128 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33129     if (btn == 'ok'){
33130         // process text value...
33131     }
33132 });
33133
33134 // Show a dialog using config options:
33135 Roo.Msg.show({
33136    title:'Save Changes?',
33137    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33138    buttons: Roo.Msg.YESNOCANCEL,
33139    fn: processResult,
33140    animEl: 'elId'
33141 });
33142 </code></pre>
33143  * @singleton
33144  */
33145 Roo.MessageBox = function(){
33146     var dlg, opt, mask, waitTimer;
33147     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33148     var buttons, activeTextEl, bwidth;
33149
33150     // private
33151     var handleButton = function(button){
33152         dlg.hide();
33153         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33154     };
33155
33156     // private
33157     var handleHide = function(){
33158         if(opt && opt.cls){
33159             dlg.el.removeClass(opt.cls);
33160         }
33161         if(waitTimer){
33162             Roo.TaskMgr.stop(waitTimer);
33163             waitTimer = null;
33164         }
33165     };
33166
33167     // private
33168     var updateButtons = function(b){
33169         var width = 0;
33170         if(!b){
33171             buttons["ok"].hide();
33172             buttons["cancel"].hide();
33173             buttons["yes"].hide();
33174             buttons["no"].hide();
33175             dlg.footer.dom.style.display = 'none';
33176             return width;
33177         }
33178         dlg.footer.dom.style.display = '';
33179         for(var k in buttons){
33180             if(typeof buttons[k] != "function"){
33181                 if(b[k]){
33182                     buttons[k].show();
33183                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33184                     width += buttons[k].el.getWidth()+15;
33185                 }else{
33186                     buttons[k].hide();
33187                 }
33188             }
33189         }
33190         return width;
33191     };
33192
33193     // private
33194     var handleEsc = function(d, k, e){
33195         if(opt && opt.closable !== false){
33196             dlg.hide();
33197         }
33198         if(e){
33199             e.stopEvent();
33200         }
33201     };
33202
33203     return {
33204         /**
33205          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33206          * @return {Roo.BasicDialog} The BasicDialog element
33207          */
33208         getDialog : function(){
33209            if(!dlg){
33210                 dlg = new Roo.BasicDialog("x-msg-box", {
33211                     autoCreate : true,
33212                     shadow: true,
33213                     draggable: true,
33214                     resizable:false,
33215                     constraintoviewport:false,
33216                     fixedcenter:true,
33217                     collapsible : false,
33218                     shim:true,
33219                     modal: true,
33220                     width:400, height:100,
33221                     buttonAlign:"center",
33222                     closeClick : function(){
33223                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33224                             handleButton("no");
33225                         }else{
33226                             handleButton("cancel");
33227                         }
33228                     }
33229                 });
33230                 dlg.on("hide", handleHide);
33231                 mask = dlg.mask;
33232                 dlg.addKeyListener(27, handleEsc);
33233                 buttons = {};
33234                 var bt = this.buttonText;
33235                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33236                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33237                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33238                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33239                 bodyEl = dlg.body.createChild({
33240
33241                     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>'
33242                 });
33243                 msgEl = bodyEl.dom.firstChild;
33244                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33245                 textboxEl.enableDisplayMode();
33246                 textboxEl.addKeyListener([10,13], function(){
33247                     if(dlg.isVisible() && opt && opt.buttons){
33248                         if(opt.buttons.ok){
33249                             handleButton("ok");
33250                         }else if(opt.buttons.yes){
33251                             handleButton("yes");
33252                         }
33253                     }
33254                 });
33255                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33256                 textareaEl.enableDisplayMode();
33257                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33258                 progressEl.enableDisplayMode();
33259                 var pf = progressEl.dom.firstChild;
33260                 if (pf) {
33261                     pp = Roo.get(pf.firstChild);
33262                     pp.setHeight(pf.offsetHeight);
33263                 }
33264                 
33265             }
33266             return dlg;
33267         },
33268
33269         /**
33270          * Updates the message box body text
33271          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33272          * the XHTML-compliant non-breaking space character '&amp;#160;')
33273          * @return {Roo.MessageBox} This message box
33274          */
33275         updateText : function(text){
33276             if(!dlg.isVisible() && !opt.width){
33277                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33278             }
33279             msgEl.innerHTML = text || '&#160;';
33280       
33281             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33282             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33283             var w = Math.max(
33284                     Math.min(opt.width || cw , this.maxWidth), 
33285                     Math.max(opt.minWidth || this.minWidth, bwidth)
33286             );
33287             if(opt.prompt){
33288                 activeTextEl.setWidth(w);
33289             }
33290             if(dlg.isVisible()){
33291                 dlg.fixedcenter = false;
33292             }
33293             // to big, make it scroll. = But as usual stupid IE does not support
33294             // !important..
33295             
33296             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33297                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33298                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33299             } else {
33300                 bodyEl.dom.style.height = '';
33301                 bodyEl.dom.style.overflowY = '';
33302             }
33303             if (cw > w) {
33304                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33305             } else {
33306                 bodyEl.dom.style.overflowX = '';
33307             }
33308             
33309             dlg.setContentSize(w, bodyEl.getHeight());
33310             if(dlg.isVisible()){
33311                 dlg.fixedcenter = true;
33312             }
33313             return this;
33314         },
33315
33316         /**
33317          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33318          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33319          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33320          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33321          * @return {Roo.MessageBox} This message box
33322          */
33323         updateProgress : function(value, text){
33324             if(text){
33325                 this.updateText(text);
33326             }
33327             if (pp) { // weird bug on my firefox - for some reason this is not defined
33328                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33329             }
33330             return this;
33331         },        
33332
33333         /**
33334          * Returns true if the message box is currently displayed
33335          * @return {Boolean} True if the message box is visible, else false
33336          */
33337         isVisible : function(){
33338             return dlg && dlg.isVisible();  
33339         },
33340
33341         /**
33342          * Hides the message box if it is displayed
33343          */
33344         hide : function(){
33345             if(this.isVisible()){
33346                 dlg.hide();
33347             }  
33348         },
33349
33350         /**
33351          * Displays a new message box, or reinitializes an existing message box, based on the config options
33352          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33353          * The following config object properties are supported:
33354          * <pre>
33355 Property    Type             Description
33356 ----------  ---------------  ------------------------------------------------------------------------------------
33357 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33358                                    closes (defaults to undefined)
33359 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33360                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33361 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33362                                    progress and wait dialogs will ignore this property and always hide the
33363                                    close button as they can only be closed programmatically.
33364 cls               String           A custom CSS class to apply to the message box element
33365 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33366                                    displayed (defaults to 75)
33367 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33368                                    function will be btn (the name of the button that was clicked, if applicable,
33369                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33370                                    Progress and wait dialogs will ignore this option since they do not respond to
33371                                    user actions and can only be closed programmatically, so any required function
33372                                    should be called by the same code after it closes the dialog.
33373 icon              String           A CSS class that provides a background image to be used as an icon for
33374                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33375 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33376 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33377 modal             Boolean          False to allow user interaction with the page while the message box is
33378                                    displayed (defaults to true)
33379 msg               String           A string that will replace the existing message box body text (defaults
33380                                    to the XHTML-compliant non-breaking space character '&#160;')
33381 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33382 progress          Boolean          True to display a progress bar (defaults to false)
33383 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33384 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33385 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33386 title             String           The title text
33387 value             String           The string value to set into the active textbox element if displayed
33388 wait              Boolean          True to display a progress bar (defaults to false)
33389 width             Number           The width of the dialog in pixels
33390 </pre>
33391          *
33392          * Example usage:
33393          * <pre><code>
33394 Roo.Msg.show({
33395    title: 'Address',
33396    msg: 'Please enter your address:',
33397    width: 300,
33398    buttons: Roo.MessageBox.OKCANCEL,
33399    multiline: true,
33400    fn: saveAddress,
33401    animEl: 'addAddressBtn'
33402 });
33403 </code></pre>
33404          * @param {Object} config Configuration options
33405          * @return {Roo.MessageBox} This message box
33406          */
33407         show : function(options)
33408         {
33409             
33410             // this causes nightmares if you show one dialog after another
33411             // especially on callbacks..
33412              
33413             if(this.isVisible()){
33414                 
33415                 this.hide();
33416                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33417                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33418                 Roo.log("New Dialog Message:" +  options.msg )
33419                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33420                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33421                 
33422             }
33423             var d = this.getDialog();
33424             opt = options;
33425             d.setTitle(opt.title || "&#160;");
33426             d.close.setDisplayed(opt.closable !== false);
33427             activeTextEl = textboxEl;
33428             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33429             if(opt.prompt){
33430                 if(opt.multiline){
33431                     textboxEl.hide();
33432                     textareaEl.show();
33433                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33434                         opt.multiline : this.defaultTextHeight);
33435                     activeTextEl = textareaEl;
33436                 }else{
33437                     textboxEl.show();
33438                     textareaEl.hide();
33439                 }
33440             }else{
33441                 textboxEl.hide();
33442                 textareaEl.hide();
33443             }
33444             progressEl.setDisplayed(opt.progress === true);
33445             this.updateProgress(0);
33446             activeTextEl.dom.value = opt.value || "";
33447             if(opt.prompt){
33448                 dlg.setDefaultButton(activeTextEl);
33449             }else{
33450                 var bs = opt.buttons;
33451                 var db = null;
33452                 if(bs && bs.ok){
33453                     db = buttons["ok"];
33454                 }else if(bs && bs.yes){
33455                     db = buttons["yes"];
33456                 }
33457                 dlg.setDefaultButton(db);
33458             }
33459             bwidth = updateButtons(opt.buttons);
33460             this.updateText(opt.msg);
33461             if(opt.cls){
33462                 d.el.addClass(opt.cls);
33463             }
33464             d.proxyDrag = opt.proxyDrag === true;
33465             d.modal = opt.modal !== false;
33466             d.mask = opt.modal !== false ? mask : false;
33467             if(!d.isVisible()){
33468                 // force it to the end of the z-index stack so it gets a cursor in FF
33469                 document.body.appendChild(dlg.el.dom);
33470                 d.animateTarget = null;
33471                 d.show(options.animEl);
33472             }
33473             return this;
33474         },
33475
33476         /**
33477          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33478          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33479          * and closing the message box when the process is complete.
33480          * @param {String} title The title bar text
33481          * @param {String} msg The message box body text
33482          * @return {Roo.MessageBox} This message box
33483          */
33484         progress : function(title, msg){
33485             this.show({
33486                 title : title,
33487                 msg : msg,
33488                 buttons: false,
33489                 progress:true,
33490                 closable:false,
33491                 minWidth: this.minProgressWidth,
33492                 modal : true
33493             });
33494             return this;
33495         },
33496
33497         /**
33498          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33499          * If a callback function is passed it will be called after the user clicks the button, and the
33500          * id of the button that was clicked will be passed as the only parameter to the callback
33501          * (could also be the top-right close button).
33502          * @param {String} title The title bar text
33503          * @param {String} msg The message box body text
33504          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33505          * @param {Object} scope (optional) The scope of the callback function
33506          * @return {Roo.MessageBox} This message box
33507          */
33508         alert : function(title, msg, fn, scope){
33509             this.show({
33510                 title : title,
33511                 msg : msg,
33512                 buttons: this.OK,
33513                 fn: fn,
33514                 scope : scope,
33515                 modal : true
33516             });
33517             return this;
33518         },
33519
33520         /**
33521          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33522          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33523          * You are responsible for closing the message box when the process is complete.
33524          * @param {String} msg The message box body text
33525          * @param {String} title (optional) The title bar text
33526          * @return {Roo.MessageBox} This message box
33527          */
33528         wait : function(msg, title){
33529             this.show({
33530                 title : title,
33531                 msg : msg,
33532                 buttons: false,
33533                 closable:false,
33534                 progress:true,
33535                 modal:true,
33536                 width:300,
33537                 wait:true
33538             });
33539             waitTimer = Roo.TaskMgr.start({
33540                 run: function(i){
33541                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33542                 },
33543                 interval: 1000
33544             });
33545             return this;
33546         },
33547
33548         /**
33549          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33550          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33551          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33552          * @param {String} title The title bar text
33553          * @param {String} msg The message box body text
33554          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33555          * @param {Object} scope (optional) The scope of the callback function
33556          * @return {Roo.MessageBox} This message box
33557          */
33558         confirm : function(title, msg, fn, scope){
33559             this.show({
33560                 title : title,
33561                 msg : msg,
33562                 buttons: this.YESNO,
33563                 fn: fn,
33564                 scope : scope,
33565                 modal : true
33566             });
33567             return this;
33568         },
33569
33570         /**
33571          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33572          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33573          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33574          * (could also be the top-right close button) and the text that was entered will be passed as the two
33575          * parameters to the callback.
33576          * @param {String} title The title bar text
33577          * @param {String} msg The message box body text
33578          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33579          * @param {Object} scope (optional) The scope of the callback function
33580          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33581          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33582          * @return {Roo.MessageBox} This message box
33583          */
33584         prompt : function(title, msg, fn, scope, multiline){
33585             this.show({
33586                 title : title,
33587                 msg : msg,
33588                 buttons: this.OKCANCEL,
33589                 fn: fn,
33590                 minWidth:250,
33591                 scope : scope,
33592                 prompt:true,
33593                 multiline: multiline,
33594                 modal : true
33595             });
33596             return this;
33597         },
33598
33599         /**
33600          * Button config that displays a single OK button
33601          * @type Object
33602          */
33603         OK : {ok:true},
33604         /**
33605          * Button config that displays Yes and No buttons
33606          * @type Object
33607          */
33608         YESNO : {yes:true, no:true},
33609         /**
33610          * Button config that displays OK and Cancel buttons
33611          * @type Object
33612          */
33613         OKCANCEL : {ok:true, cancel:true},
33614         /**
33615          * Button config that displays Yes, No and Cancel buttons
33616          * @type Object
33617          */
33618         YESNOCANCEL : {yes:true, no:true, cancel:true},
33619
33620         /**
33621          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33622          * @type Number
33623          */
33624         defaultTextHeight : 75,
33625         /**
33626          * The maximum width in pixels of the message box (defaults to 600)
33627          * @type Number
33628          */
33629         maxWidth : 600,
33630         /**
33631          * The minimum width in pixels of the message box (defaults to 100)
33632          * @type Number
33633          */
33634         minWidth : 100,
33635         /**
33636          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33637          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33638          * @type Number
33639          */
33640         minProgressWidth : 250,
33641         /**
33642          * An object containing the default button text strings that can be overriden for localized language support.
33643          * Supported properties are: ok, cancel, yes and no.
33644          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33645          * @type Object
33646          */
33647         buttonText : {
33648             ok : "OK",
33649             cancel : "Cancel",
33650             yes : "Yes",
33651             no : "No"
33652         }
33653     };
33654 }();
33655
33656 /**
33657  * Shorthand for {@link Roo.MessageBox}
33658  */
33659 Roo.Msg = Roo.MessageBox;/*
33660  * Based on:
33661  * Ext JS Library 1.1.1
33662  * Copyright(c) 2006-2007, Ext JS, LLC.
33663  *
33664  * Originally Released Under LGPL - original licence link has changed is not relivant.
33665  *
33666  * Fork - LGPL
33667  * <script type="text/javascript">
33668  */
33669 /**
33670  * @class Roo.QuickTips
33671  * Provides attractive and customizable tooltips for any element.
33672  * @singleton
33673  */
33674 Roo.QuickTips = function(){
33675     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33676     var ce, bd, xy, dd;
33677     var visible = false, disabled = true, inited = false;
33678     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33679     
33680     var onOver = function(e){
33681         if(disabled){
33682             return;
33683         }
33684         var t = e.getTarget();
33685         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33686             return;
33687         }
33688         if(ce && t == ce.el){
33689             clearTimeout(hideProc);
33690             return;
33691         }
33692         if(t && tagEls[t.id]){
33693             tagEls[t.id].el = t;
33694             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33695             return;
33696         }
33697         var ttp, et = Roo.fly(t);
33698         var ns = cfg.namespace;
33699         if(tm.interceptTitles && t.title){
33700             ttp = t.title;
33701             t.qtip = ttp;
33702             t.removeAttribute("title");
33703             e.preventDefault();
33704         }else{
33705             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33706         }
33707         if(ttp){
33708             showProc = show.defer(tm.showDelay, tm, [{
33709                 el: t, 
33710                 text: ttp.replace(/\\n/g,'<br/>'),
33711                 width: et.getAttributeNS(ns, cfg.width),
33712                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33713                 title: et.getAttributeNS(ns, cfg.title),
33714                     cls: et.getAttributeNS(ns, cfg.cls)
33715             }]);
33716         }
33717     };
33718     
33719     var onOut = function(e){
33720         clearTimeout(showProc);
33721         var t = e.getTarget();
33722         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33723             hideProc = setTimeout(hide, tm.hideDelay);
33724         }
33725     };
33726     
33727     var onMove = function(e){
33728         if(disabled){
33729             return;
33730         }
33731         xy = e.getXY();
33732         xy[1] += 18;
33733         if(tm.trackMouse && ce){
33734             el.setXY(xy);
33735         }
33736     };
33737     
33738     var onDown = function(e){
33739         clearTimeout(showProc);
33740         clearTimeout(hideProc);
33741         if(!e.within(el)){
33742             if(tm.hideOnClick){
33743                 hide();
33744                 tm.disable();
33745                 tm.enable.defer(100, tm);
33746             }
33747         }
33748     };
33749     
33750     var getPad = function(){
33751         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33752     };
33753
33754     var show = function(o){
33755         if(disabled){
33756             return;
33757         }
33758         clearTimeout(dismissProc);
33759         ce = o;
33760         if(removeCls){ // in case manually hidden
33761             el.removeClass(removeCls);
33762             removeCls = null;
33763         }
33764         if(ce.cls){
33765             el.addClass(ce.cls);
33766             removeCls = ce.cls;
33767         }
33768         if(ce.title){
33769             tipTitle.update(ce.title);
33770             tipTitle.show();
33771         }else{
33772             tipTitle.update('');
33773             tipTitle.hide();
33774         }
33775         el.dom.style.width  = tm.maxWidth+'px';
33776         //tipBody.dom.style.width = '';
33777         tipBodyText.update(o.text);
33778         var p = getPad(), w = ce.width;
33779         if(!w){
33780             var td = tipBodyText.dom;
33781             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33782             if(aw > tm.maxWidth){
33783                 w = tm.maxWidth;
33784             }else if(aw < tm.minWidth){
33785                 w = tm.minWidth;
33786             }else{
33787                 w = aw;
33788             }
33789         }
33790         //tipBody.setWidth(w);
33791         el.setWidth(parseInt(w, 10) + p);
33792         if(ce.autoHide === false){
33793             close.setDisplayed(true);
33794             if(dd){
33795                 dd.unlock();
33796             }
33797         }else{
33798             close.setDisplayed(false);
33799             if(dd){
33800                 dd.lock();
33801             }
33802         }
33803         if(xy){
33804             el.avoidY = xy[1]-18;
33805             el.setXY(xy);
33806         }
33807         if(tm.animate){
33808             el.setOpacity(.1);
33809             el.setStyle("visibility", "visible");
33810             el.fadeIn({callback: afterShow});
33811         }else{
33812             afterShow();
33813         }
33814     };
33815     
33816     var afterShow = function(){
33817         if(ce){
33818             el.show();
33819             esc.enable();
33820             if(tm.autoDismiss && ce.autoHide !== false){
33821                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33822             }
33823         }
33824     };
33825     
33826     var hide = function(noanim){
33827         clearTimeout(dismissProc);
33828         clearTimeout(hideProc);
33829         ce = null;
33830         if(el.isVisible()){
33831             esc.disable();
33832             if(noanim !== true && tm.animate){
33833                 el.fadeOut({callback: afterHide});
33834             }else{
33835                 afterHide();
33836             } 
33837         }
33838     };
33839     
33840     var afterHide = function(){
33841         el.hide();
33842         if(removeCls){
33843             el.removeClass(removeCls);
33844             removeCls = null;
33845         }
33846     };
33847     
33848     return {
33849         /**
33850         * @cfg {Number} minWidth
33851         * The minimum width of the quick tip (defaults to 40)
33852         */
33853        minWidth : 40,
33854         /**
33855         * @cfg {Number} maxWidth
33856         * The maximum width of the quick tip (defaults to 300)
33857         */
33858        maxWidth : 300,
33859         /**
33860         * @cfg {Boolean} interceptTitles
33861         * True to automatically use the element's DOM title value if available (defaults to false)
33862         */
33863        interceptTitles : false,
33864         /**
33865         * @cfg {Boolean} trackMouse
33866         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33867         */
33868        trackMouse : false,
33869         /**
33870         * @cfg {Boolean} hideOnClick
33871         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33872         */
33873        hideOnClick : true,
33874         /**
33875         * @cfg {Number} showDelay
33876         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33877         */
33878        showDelay : 500,
33879         /**
33880         * @cfg {Number} hideDelay
33881         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33882         */
33883        hideDelay : 200,
33884         /**
33885         * @cfg {Boolean} autoHide
33886         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33887         * Used in conjunction with hideDelay.
33888         */
33889        autoHide : true,
33890         /**
33891         * @cfg {Boolean}
33892         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33893         * (defaults to true).  Used in conjunction with autoDismissDelay.
33894         */
33895        autoDismiss : true,
33896         /**
33897         * @cfg {Number}
33898         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33899         */
33900        autoDismissDelay : 5000,
33901        /**
33902         * @cfg {Boolean} animate
33903         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33904         */
33905        animate : false,
33906
33907        /**
33908         * @cfg {String} title
33909         * Title text to display (defaults to '').  This can be any valid HTML markup.
33910         */
33911         title: '',
33912        /**
33913         * @cfg {String} text
33914         * Body text to display (defaults to '').  This can be any valid HTML markup.
33915         */
33916         text : '',
33917        /**
33918         * @cfg {String} cls
33919         * A CSS class to apply to the base quick tip element (defaults to '').
33920         */
33921         cls : '',
33922        /**
33923         * @cfg {Number} width
33924         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33925         * minWidth or maxWidth.
33926         */
33927         width : null,
33928
33929     /**
33930      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33931      * or display QuickTips in a page.
33932      */
33933        init : function(){
33934           tm = Roo.QuickTips;
33935           cfg = tm.tagConfig;
33936           if(!inited){
33937               if(!Roo.isReady){ // allow calling of init() before onReady
33938                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33939                   return;
33940               }
33941               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33942               el.fxDefaults = {stopFx: true};
33943               // maximum custom styling
33944               //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>');
33945               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>');              
33946               tipTitle = el.child('h3');
33947               tipTitle.enableDisplayMode("block");
33948               tipBody = el.child('div.x-tip-bd');
33949               tipBodyText = el.child('div.x-tip-bd-inner');
33950               //bdLeft = el.child('div.x-tip-bd-left');
33951               //bdRight = el.child('div.x-tip-bd-right');
33952               close = el.child('div.x-tip-close');
33953               close.enableDisplayMode("block");
33954               close.on("click", hide);
33955               var d = Roo.get(document);
33956               d.on("mousedown", onDown);
33957               d.on("mouseover", onOver);
33958               d.on("mouseout", onOut);
33959               d.on("mousemove", onMove);
33960               esc = d.addKeyListener(27, hide);
33961               esc.disable();
33962               if(Roo.dd.DD){
33963                   dd = el.initDD("default", null, {
33964                       onDrag : function(){
33965                           el.sync();  
33966                       }
33967                   });
33968                   dd.setHandleElId(tipTitle.id);
33969                   dd.lock();
33970               }
33971               inited = true;
33972           }
33973           this.enable(); 
33974        },
33975
33976     /**
33977      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33978      * are supported:
33979      * <pre>
33980 Property    Type                   Description
33981 ----------  ---------------------  ------------------------------------------------------------------------
33982 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33983      * </ul>
33984      * @param {Object} config The config object
33985      */
33986        register : function(config){
33987            var cs = config instanceof Array ? config : arguments;
33988            for(var i = 0, len = cs.length; i < len; i++) {
33989                var c = cs[i];
33990                var target = c.target;
33991                if(target){
33992                    if(target instanceof Array){
33993                        for(var j = 0, jlen = target.length; j < jlen; j++){
33994                            tagEls[target[j]] = c;
33995                        }
33996                    }else{
33997                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33998                    }
33999                }
34000            }
34001        },
34002
34003     /**
34004      * Removes this quick tip from its element and destroys it.
34005      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34006      */
34007        unregister : function(el){
34008            delete tagEls[Roo.id(el)];
34009        },
34010
34011     /**
34012      * Enable this quick tip.
34013      */
34014        enable : function(){
34015            if(inited && disabled){
34016                locks.pop();
34017                if(locks.length < 1){
34018                    disabled = false;
34019                }
34020            }
34021        },
34022
34023     /**
34024      * Disable this quick tip.
34025      */
34026        disable : function(){
34027           disabled = true;
34028           clearTimeout(showProc);
34029           clearTimeout(hideProc);
34030           clearTimeout(dismissProc);
34031           if(ce){
34032               hide(true);
34033           }
34034           locks.push(1);
34035        },
34036
34037     /**
34038      * Returns true if the quick tip is enabled, else false.
34039      */
34040        isEnabled : function(){
34041             return !disabled;
34042        },
34043
34044         // private
34045        tagConfig : {
34046            namespace : "roo", // was ext?? this may break..
34047            alt_namespace : "ext",
34048            attribute : "qtip",
34049            width : "width",
34050            target : "target",
34051            title : "qtitle",
34052            hide : "hide",
34053            cls : "qclass"
34054        }
34055    };
34056 }();
34057
34058 // backwards compat
34059 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34060  * Based on:
34061  * Ext JS Library 1.1.1
34062  * Copyright(c) 2006-2007, Ext JS, LLC.
34063  *
34064  * Originally Released Under LGPL - original licence link has changed is not relivant.
34065  *
34066  * Fork - LGPL
34067  * <script type="text/javascript">
34068  */
34069  
34070
34071 /**
34072  * @class Roo.tree.TreePanel
34073  * @extends Roo.data.Tree
34074
34075  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34076  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34077  * @cfg {Boolean} enableDD true to enable drag and drop
34078  * @cfg {Boolean} enableDrag true to enable just drag
34079  * @cfg {Boolean} enableDrop true to enable just drop
34080  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34081  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34082  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34083  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34084  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34085  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34086  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34087  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34088  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34089  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34090  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34091  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34092  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34093  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34094  * @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>
34095  * @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>
34096  * 
34097  * @constructor
34098  * @param {String/HTMLElement/Element} el The container element
34099  * @param {Object} config
34100  */
34101 Roo.tree.TreePanel = function(el, config){
34102     var root = false;
34103     var loader = false;
34104     if (config.root) {
34105         root = config.root;
34106         delete config.root;
34107     }
34108     if (config.loader) {
34109         loader = config.loader;
34110         delete config.loader;
34111     }
34112     
34113     Roo.apply(this, config);
34114     Roo.tree.TreePanel.superclass.constructor.call(this);
34115     this.el = Roo.get(el);
34116     this.el.addClass('x-tree');
34117     //console.log(root);
34118     if (root) {
34119         this.setRootNode( Roo.factory(root, Roo.tree));
34120     }
34121     if (loader) {
34122         this.loader = Roo.factory(loader, Roo.tree);
34123     }
34124    /**
34125     * Read-only. The id of the container element becomes this TreePanel's id.
34126     */
34127     this.id = this.el.id;
34128     this.addEvents({
34129         /**
34130         * @event beforeload
34131         * Fires before a node is loaded, return false to cancel
34132         * @param {Node} node The node being loaded
34133         */
34134         "beforeload" : true,
34135         /**
34136         * @event load
34137         * Fires when a node is loaded
34138         * @param {Node} node The node that was loaded
34139         */
34140         "load" : true,
34141         /**
34142         * @event textchange
34143         * Fires when the text for a node is changed
34144         * @param {Node} node The node
34145         * @param {String} text The new text
34146         * @param {String} oldText The old text
34147         */
34148         "textchange" : true,
34149         /**
34150         * @event beforeexpand
34151         * Fires before a node is expanded, return false to cancel.
34152         * @param {Node} node The node
34153         * @param {Boolean} deep
34154         * @param {Boolean} anim
34155         */
34156         "beforeexpand" : true,
34157         /**
34158         * @event beforecollapse
34159         * Fires before a node is collapsed, return false to cancel.
34160         * @param {Node} node The node
34161         * @param {Boolean} deep
34162         * @param {Boolean} anim
34163         */
34164         "beforecollapse" : true,
34165         /**
34166         * @event expand
34167         * Fires when a node is expanded
34168         * @param {Node} node The node
34169         */
34170         "expand" : true,
34171         /**
34172         * @event disabledchange
34173         * Fires when the disabled status of a node changes
34174         * @param {Node} node The node
34175         * @param {Boolean} disabled
34176         */
34177         "disabledchange" : true,
34178         /**
34179         * @event collapse
34180         * Fires when a node is collapsed
34181         * @param {Node} node The node
34182         */
34183         "collapse" : true,
34184         /**
34185         * @event beforeclick
34186         * Fires before click processing on a node. Return false to cancel the default action.
34187         * @param {Node} node The node
34188         * @param {Roo.EventObject} e The event object
34189         */
34190         "beforeclick":true,
34191         /**
34192         * @event checkchange
34193         * Fires when a node with a checkbox's checked property changes
34194         * @param {Node} this This node
34195         * @param {Boolean} checked
34196         */
34197         "checkchange":true,
34198         /**
34199         * @event click
34200         * Fires when a node is clicked
34201         * @param {Node} node The node
34202         * @param {Roo.EventObject} e The event object
34203         */
34204         "click":true,
34205         /**
34206         * @event dblclick
34207         * Fires when a node is double clicked
34208         * @param {Node} node The node
34209         * @param {Roo.EventObject} e The event object
34210         */
34211         "dblclick":true,
34212         /**
34213         * @event contextmenu
34214         * Fires when a node is right clicked
34215         * @param {Node} node The node
34216         * @param {Roo.EventObject} e The event object
34217         */
34218         "contextmenu":true,
34219         /**
34220         * @event beforechildrenrendered
34221         * Fires right before the child nodes for a node are rendered
34222         * @param {Node} node The node
34223         */
34224         "beforechildrenrendered":true,
34225         /**
34226         * @event startdrag
34227         * Fires when a node starts being dragged
34228         * @param {Roo.tree.TreePanel} this
34229         * @param {Roo.tree.TreeNode} node
34230         * @param {event} e The raw browser event
34231         */ 
34232        "startdrag" : true,
34233        /**
34234         * @event enddrag
34235         * Fires when a drag operation is complete
34236         * @param {Roo.tree.TreePanel} this
34237         * @param {Roo.tree.TreeNode} node
34238         * @param {event} e The raw browser event
34239         */
34240        "enddrag" : true,
34241        /**
34242         * @event dragdrop
34243         * Fires when a dragged node is dropped on a valid DD target
34244         * @param {Roo.tree.TreePanel} this
34245         * @param {Roo.tree.TreeNode} node
34246         * @param {DD} dd The dd it was dropped on
34247         * @param {event} e The raw browser event
34248         */
34249        "dragdrop" : true,
34250        /**
34251         * @event beforenodedrop
34252         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34253         * passed to handlers has the following properties:<br />
34254         * <ul style="padding:5px;padding-left:16px;">
34255         * <li>tree - The TreePanel</li>
34256         * <li>target - The node being targeted for the drop</li>
34257         * <li>data - The drag data from the drag source</li>
34258         * <li>point - The point of the drop - append, above or below</li>
34259         * <li>source - The drag source</li>
34260         * <li>rawEvent - Raw mouse event</li>
34261         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34262         * to be inserted by setting them on this object.</li>
34263         * <li>cancel - Set this to true to cancel the drop.</li>
34264         * </ul>
34265         * @param {Object} dropEvent
34266         */
34267        "beforenodedrop" : true,
34268        /**
34269         * @event nodedrop
34270         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34271         * passed to handlers has the following properties:<br />
34272         * <ul style="padding:5px;padding-left:16px;">
34273         * <li>tree - The TreePanel</li>
34274         * <li>target - The node being targeted for the drop</li>
34275         * <li>data - The drag data from the drag source</li>
34276         * <li>point - The point of the drop - append, above or below</li>
34277         * <li>source - The drag source</li>
34278         * <li>rawEvent - Raw mouse event</li>
34279         * <li>dropNode - Dropped node(s).</li>
34280         * </ul>
34281         * @param {Object} dropEvent
34282         */
34283        "nodedrop" : true,
34284         /**
34285         * @event nodedragover
34286         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
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 - Drop node(s) provided by the source.</li>
34296         * <li>cancel - Set this to true to signal drop not allowed.</li>
34297         * </ul>
34298         * @param {Object} dragOverEvent
34299         */
34300        "nodedragover" : true,
34301        /**
34302         * @event appendnode
34303         * Fires when append node to the tree
34304         * @param {Roo.tree.TreePanel} this
34305         * @param {Roo.tree.TreeNode} node
34306         * @param {Number} index The index of the newly appended node
34307         */
34308        "appendnode" : true
34309         
34310     });
34311     if(this.singleExpand){
34312        this.on("beforeexpand", this.restrictExpand, this);
34313     }
34314     if (this.editor) {
34315         this.editor.tree = this;
34316         this.editor = Roo.factory(this.editor, Roo.tree);
34317     }
34318     
34319     if (this.selModel) {
34320         this.selModel = Roo.factory(this.selModel, Roo.tree);
34321     }
34322    
34323 };
34324 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34325     rootVisible : true,
34326     animate: Roo.enableFx,
34327     lines : true,
34328     enableDD : false,
34329     hlDrop : Roo.enableFx,
34330   
34331     renderer: false,
34332     
34333     rendererTip: false,
34334     // private
34335     restrictExpand : function(node){
34336         var p = node.parentNode;
34337         if(p){
34338             if(p.expandedChild && p.expandedChild.parentNode == p){
34339                 p.expandedChild.collapse();
34340             }
34341             p.expandedChild = node;
34342         }
34343     },
34344
34345     // private override
34346     setRootNode : function(node){
34347         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34348         if(!this.rootVisible){
34349             node.ui = new Roo.tree.RootTreeNodeUI(node);
34350         }
34351         return node;
34352     },
34353
34354     /**
34355      * Returns the container element for this TreePanel
34356      */
34357     getEl : function(){
34358         return this.el;
34359     },
34360
34361     /**
34362      * Returns the default TreeLoader for this TreePanel
34363      */
34364     getLoader : function(){
34365         return this.loader;
34366     },
34367
34368     /**
34369      * Expand all nodes
34370      */
34371     expandAll : function(){
34372         this.root.expand(true);
34373     },
34374
34375     /**
34376      * Collapse all nodes
34377      */
34378     collapseAll : function(){
34379         this.root.collapse(true);
34380     },
34381
34382     /**
34383      * Returns the selection model used by this TreePanel
34384      */
34385     getSelectionModel : function(){
34386         if(!this.selModel){
34387             this.selModel = new Roo.tree.DefaultSelectionModel();
34388         }
34389         return this.selModel;
34390     },
34391
34392     /**
34393      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34394      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34395      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34396      * @return {Array}
34397      */
34398     getChecked : function(a, startNode){
34399         startNode = startNode || this.root;
34400         var r = [];
34401         var f = function(){
34402             if(this.attributes.checked){
34403                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34404             }
34405         }
34406         startNode.cascade(f);
34407         return r;
34408     },
34409
34410     /**
34411      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34412      * @param {String} path
34413      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34414      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34415      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34416      */
34417     expandPath : function(path, attr, callback){
34418         attr = attr || "id";
34419         var keys = path.split(this.pathSeparator);
34420         var curNode = this.root;
34421         if(curNode.attributes[attr] != keys[1]){ // invalid root
34422             if(callback){
34423                 callback(false, null);
34424             }
34425             return;
34426         }
34427         var index = 1;
34428         var f = function(){
34429             if(++index == keys.length){
34430                 if(callback){
34431                     callback(true, curNode);
34432                 }
34433                 return;
34434             }
34435             var c = curNode.findChild(attr, keys[index]);
34436             if(!c){
34437                 if(callback){
34438                     callback(false, curNode);
34439                 }
34440                 return;
34441             }
34442             curNode = c;
34443             c.expand(false, false, f);
34444         };
34445         curNode.expand(false, false, f);
34446     },
34447
34448     /**
34449      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34450      * @param {String} path
34451      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34452      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34453      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34454      */
34455     selectPath : function(path, attr, callback){
34456         attr = attr || "id";
34457         var keys = path.split(this.pathSeparator);
34458         var v = keys.pop();
34459         if(keys.length > 0){
34460             var f = function(success, node){
34461                 if(success && node){
34462                     var n = node.findChild(attr, v);
34463                     if(n){
34464                         n.select();
34465                         if(callback){
34466                             callback(true, n);
34467                         }
34468                     }else if(callback){
34469                         callback(false, n);
34470                     }
34471                 }else{
34472                     if(callback){
34473                         callback(false, n);
34474                     }
34475                 }
34476             };
34477             this.expandPath(keys.join(this.pathSeparator), attr, f);
34478         }else{
34479             this.root.select();
34480             if(callback){
34481                 callback(true, this.root);
34482             }
34483         }
34484     },
34485
34486     getTreeEl : function(){
34487         return this.el;
34488     },
34489
34490     /**
34491      * Trigger rendering of this TreePanel
34492      */
34493     render : function(){
34494         if (this.innerCt) {
34495             return this; // stop it rendering more than once!!
34496         }
34497         
34498         this.innerCt = this.el.createChild({tag:"ul",
34499                cls:"x-tree-root-ct " +
34500                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34501
34502         if(this.containerScroll){
34503             Roo.dd.ScrollManager.register(this.el);
34504         }
34505         if((this.enableDD || this.enableDrop) && !this.dropZone){
34506            /**
34507             * The dropZone used by this tree if drop is enabled
34508             * @type Roo.tree.TreeDropZone
34509             */
34510              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34511                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34512            });
34513         }
34514         if((this.enableDD || this.enableDrag) && !this.dragZone){
34515            /**
34516             * The dragZone used by this tree if drag is enabled
34517             * @type Roo.tree.TreeDragZone
34518             */
34519             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34520                ddGroup: this.ddGroup || "TreeDD",
34521                scroll: this.ddScroll
34522            });
34523         }
34524         this.getSelectionModel().init(this);
34525         if (!this.root) {
34526             Roo.log("ROOT not set in tree");
34527             return this;
34528         }
34529         this.root.render();
34530         if(!this.rootVisible){
34531             this.root.renderChildren();
34532         }
34533         return this;
34534     }
34535 });/*
34536  * Based on:
34537  * Ext JS Library 1.1.1
34538  * Copyright(c) 2006-2007, Ext JS, LLC.
34539  *
34540  * Originally Released Under LGPL - original licence link has changed is not relivant.
34541  *
34542  * Fork - LGPL
34543  * <script type="text/javascript">
34544  */
34545  
34546
34547 /**
34548  * @class Roo.tree.DefaultSelectionModel
34549  * @extends Roo.util.Observable
34550  * The default single selection for a TreePanel.
34551  * @param {Object} cfg Configuration
34552  */
34553 Roo.tree.DefaultSelectionModel = function(cfg){
34554    this.selNode = null;
34555    
34556    
34557    
34558    this.addEvents({
34559        /**
34560         * @event selectionchange
34561         * Fires when the selected node changes
34562         * @param {DefaultSelectionModel} this
34563         * @param {TreeNode} node the new selection
34564         */
34565        "selectionchange" : true,
34566
34567        /**
34568         * @event beforeselect
34569         * Fires before the selected node changes, return false to cancel the change
34570         * @param {DefaultSelectionModel} this
34571         * @param {TreeNode} node the new selection
34572         * @param {TreeNode} node the old selection
34573         */
34574        "beforeselect" : true
34575    });
34576    
34577     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34578 };
34579
34580 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34581     init : function(tree){
34582         this.tree = tree;
34583         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34584         tree.on("click", this.onNodeClick, this);
34585     },
34586     
34587     onNodeClick : function(node, e){
34588         if (e.ctrlKey && this.selNode == node)  {
34589             this.unselect(node);
34590             return;
34591         }
34592         this.select(node);
34593     },
34594     
34595     /**
34596      * Select a node.
34597      * @param {TreeNode} node The node to select
34598      * @return {TreeNode} The selected node
34599      */
34600     select : function(node){
34601         var last = this.selNode;
34602         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34603             if(last){
34604                 last.ui.onSelectedChange(false);
34605             }
34606             this.selNode = node;
34607             node.ui.onSelectedChange(true);
34608             this.fireEvent("selectionchange", this, node, last);
34609         }
34610         return node;
34611     },
34612     
34613     /**
34614      * Deselect a node.
34615      * @param {TreeNode} node The node to unselect
34616      */
34617     unselect : function(node){
34618         if(this.selNode == node){
34619             this.clearSelections();
34620         }    
34621     },
34622     
34623     /**
34624      * Clear all selections
34625      */
34626     clearSelections : function(){
34627         var n = this.selNode;
34628         if(n){
34629             n.ui.onSelectedChange(false);
34630             this.selNode = null;
34631             this.fireEvent("selectionchange", this, null);
34632         }
34633         return n;
34634     },
34635     
34636     /**
34637      * Get the selected node
34638      * @return {TreeNode} The selected node
34639      */
34640     getSelectedNode : function(){
34641         return this.selNode;    
34642     },
34643     
34644     /**
34645      * Returns true if the node is selected
34646      * @param {TreeNode} node The node to check
34647      * @return {Boolean}
34648      */
34649     isSelected : function(node){
34650         return this.selNode == node;  
34651     },
34652
34653     /**
34654      * Selects the node above the selected node in the tree, intelligently walking the nodes
34655      * @return TreeNode The new selection
34656      */
34657     selectPrevious : function(){
34658         var s = this.selNode || this.lastSelNode;
34659         if(!s){
34660             return null;
34661         }
34662         var ps = s.previousSibling;
34663         if(ps){
34664             if(!ps.isExpanded() || ps.childNodes.length < 1){
34665                 return this.select(ps);
34666             } else{
34667                 var lc = ps.lastChild;
34668                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34669                     lc = lc.lastChild;
34670                 }
34671                 return this.select(lc);
34672             }
34673         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34674             return this.select(s.parentNode);
34675         }
34676         return null;
34677     },
34678
34679     /**
34680      * Selects the node above the selected node in the tree, intelligently walking the nodes
34681      * @return TreeNode The new selection
34682      */
34683     selectNext : function(){
34684         var s = this.selNode || this.lastSelNode;
34685         if(!s){
34686             return null;
34687         }
34688         if(s.firstChild && s.isExpanded()){
34689              return this.select(s.firstChild);
34690          }else if(s.nextSibling){
34691              return this.select(s.nextSibling);
34692          }else if(s.parentNode){
34693             var newS = null;
34694             s.parentNode.bubble(function(){
34695                 if(this.nextSibling){
34696                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34697                     return false;
34698                 }
34699             });
34700             return newS;
34701          }
34702         return null;
34703     },
34704
34705     onKeyDown : function(e){
34706         var s = this.selNode || this.lastSelNode;
34707         // undesirable, but required
34708         var sm = this;
34709         if(!s){
34710             return;
34711         }
34712         var k = e.getKey();
34713         switch(k){
34714              case e.DOWN:
34715                  e.stopEvent();
34716                  this.selectNext();
34717              break;
34718              case e.UP:
34719                  e.stopEvent();
34720                  this.selectPrevious();
34721              break;
34722              case e.RIGHT:
34723                  e.preventDefault();
34724                  if(s.hasChildNodes()){
34725                      if(!s.isExpanded()){
34726                          s.expand();
34727                      }else if(s.firstChild){
34728                          this.select(s.firstChild, e);
34729                      }
34730                  }
34731              break;
34732              case e.LEFT:
34733                  e.preventDefault();
34734                  if(s.hasChildNodes() && s.isExpanded()){
34735                      s.collapse();
34736                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34737                      this.select(s.parentNode, e);
34738                  }
34739              break;
34740         };
34741     }
34742 });
34743
34744 /**
34745  * @class Roo.tree.MultiSelectionModel
34746  * @extends Roo.util.Observable
34747  * Multi selection for a TreePanel.
34748  * @param {Object} cfg Configuration
34749  */
34750 Roo.tree.MultiSelectionModel = function(){
34751    this.selNodes = [];
34752    this.selMap = {};
34753    this.addEvents({
34754        /**
34755         * @event selectionchange
34756         * Fires when the selected nodes change
34757         * @param {MultiSelectionModel} this
34758         * @param {Array} nodes Array of the selected nodes
34759         */
34760        "selectionchange" : true
34761    });
34762    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34763    
34764 };
34765
34766 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34767     init : function(tree){
34768         this.tree = tree;
34769         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34770         tree.on("click", this.onNodeClick, this);
34771     },
34772     
34773     onNodeClick : function(node, e){
34774         this.select(node, e, e.ctrlKey);
34775     },
34776     
34777     /**
34778      * Select a node.
34779      * @param {TreeNode} node The node to select
34780      * @param {EventObject} e (optional) An event associated with the selection
34781      * @param {Boolean} keepExisting True to retain existing selections
34782      * @return {TreeNode} The selected node
34783      */
34784     select : function(node, e, keepExisting){
34785         if(keepExisting !== true){
34786             this.clearSelections(true);
34787         }
34788         if(this.isSelected(node)){
34789             this.lastSelNode = node;
34790             return node;
34791         }
34792         this.selNodes.push(node);
34793         this.selMap[node.id] = node;
34794         this.lastSelNode = node;
34795         node.ui.onSelectedChange(true);
34796         this.fireEvent("selectionchange", this, this.selNodes);
34797         return node;
34798     },
34799     
34800     /**
34801      * Deselect a node.
34802      * @param {TreeNode} node The node to unselect
34803      */
34804     unselect : function(node){
34805         if(this.selMap[node.id]){
34806             node.ui.onSelectedChange(false);
34807             var sn = this.selNodes;
34808             var index = -1;
34809             if(sn.indexOf){
34810                 index = sn.indexOf(node);
34811             }else{
34812                 for(var i = 0, len = sn.length; i < len; i++){
34813                     if(sn[i] == node){
34814                         index = i;
34815                         break;
34816                     }
34817                 }
34818             }
34819             if(index != -1){
34820                 this.selNodes.splice(index, 1);
34821             }
34822             delete this.selMap[node.id];
34823             this.fireEvent("selectionchange", this, this.selNodes);
34824         }
34825     },
34826     
34827     /**
34828      * Clear all selections
34829      */
34830     clearSelections : function(suppressEvent){
34831         var sn = this.selNodes;
34832         if(sn.length > 0){
34833             for(var i = 0, len = sn.length; i < len; i++){
34834                 sn[i].ui.onSelectedChange(false);
34835             }
34836             this.selNodes = [];
34837             this.selMap = {};
34838             if(suppressEvent !== true){
34839                 this.fireEvent("selectionchange", this, this.selNodes);
34840             }
34841         }
34842     },
34843     
34844     /**
34845      * Returns true if the node is selected
34846      * @param {TreeNode} node The node to check
34847      * @return {Boolean}
34848      */
34849     isSelected : function(node){
34850         return this.selMap[node.id] ? true : false;  
34851     },
34852     
34853     /**
34854      * Returns an array of the selected nodes
34855      * @return {Array}
34856      */
34857     getSelectedNodes : function(){
34858         return this.selNodes;    
34859     },
34860
34861     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34862
34863     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34864
34865     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34866 });/*
34867  * Based on:
34868  * Ext JS Library 1.1.1
34869  * Copyright(c) 2006-2007, Ext JS, LLC.
34870  *
34871  * Originally Released Under LGPL - original licence link has changed is not relivant.
34872  *
34873  * Fork - LGPL
34874  * <script type="text/javascript">
34875  */
34876  
34877 /**
34878  * @class Roo.tree.TreeNode
34879  * @extends Roo.data.Node
34880  * @cfg {String} text The text for this node
34881  * @cfg {Boolean} expanded true to start the node expanded
34882  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34883  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34884  * @cfg {Boolean} disabled true to start the node disabled
34885  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34886  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
34887  * @cfg {String} cls A css class to be added to the node
34888  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34889  * @cfg {String} href URL of the link used for the node (defaults to #)
34890  * @cfg {String} hrefTarget target frame for the link
34891  * @cfg {String} qtip An Ext QuickTip for the node
34892  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34893  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34894  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34895  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34896  * (defaults to undefined with no checkbox rendered)
34897  * @constructor
34898  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34899  */
34900 Roo.tree.TreeNode = function(attributes){
34901     attributes = attributes || {};
34902     if(typeof attributes == "string"){
34903         attributes = {text: attributes};
34904     }
34905     this.childrenRendered = false;
34906     this.rendered = false;
34907     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34908     this.expanded = attributes.expanded === true;
34909     this.isTarget = attributes.isTarget !== false;
34910     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34911     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34912
34913     /**
34914      * Read-only. The text for this node. To change it use setText().
34915      * @type String
34916      */
34917     this.text = attributes.text;
34918     /**
34919      * True if this node is disabled.
34920      * @type Boolean
34921      */
34922     this.disabled = attributes.disabled === true;
34923
34924     this.addEvents({
34925         /**
34926         * @event textchange
34927         * Fires when the text for this node is changed
34928         * @param {Node} this This node
34929         * @param {String} text The new text
34930         * @param {String} oldText The old text
34931         */
34932         "textchange" : true,
34933         /**
34934         * @event beforeexpand
34935         * Fires before this node is expanded, return false to cancel.
34936         * @param {Node} this This node
34937         * @param {Boolean} deep
34938         * @param {Boolean} anim
34939         */
34940         "beforeexpand" : true,
34941         /**
34942         * @event beforecollapse
34943         * Fires before this node is collapsed, return false to cancel.
34944         * @param {Node} this This node
34945         * @param {Boolean} deep
34946         * @param {Boolean} anim
34947         */
34948         "beforecollapse" : true,
34949         /**
34950         * @event expand
34951         * Fires when this node is expanded
34952         * @param {Node} this This node
34953         */
34954         "expand" : true,
34955         /**
34956         * @event disabledchange
34957         * Fires when the disabled status of this node changes
34958         * @param {Node} this This node
34959         * @param {Boolean} disabled
34960         */
34961         "disabledchange" : true,
34962         /**
34963         * @event collapse
34964         * Fires when this node is collapsed
34965         * @param {Node} this This node
34966         */
34967         "collapse" : true,
34968         /**
34969         * @event beforeclick
34970         * Fires before click processing. Return false to cancel the default action.
34971         * @param {Node} this This node
34972         * @param {Roo.EventObject} e The event object
34973         */
34974         "beforeclick":true,
34975         /**
34976         * @event checkchange
34977         * Fires when a node with a checkbox's checked property changes
34978         * @param {Node} this This node
34979         * @param {Boolean} checked
34980         */
34981         "checkchange":true,
34982         /**
34983         * @event click
34984         * Fires when this node is clicked
34985         * @param {Node} this This node
34986         * @param {Roo.EventObject} e The event object
34987         */
34988         "click":true,
34989         /**
34990         * @event dblclick
34991         * Fires when this node is double clicked
34992         * @param {Node} this This node
34993         * @param {Roo.EventObject} e The event object
34994         */
34995         "dblclick":true,
34996         /**
34997         * @event contextmenu
34998         * Fires when this node is right clicked
34999         * @param {Node} this This node
35000         * @param {Roo.EventObject} e The event object
35001         */
35002         "contextmenu":true,
35003         /**
35004         * @event beforechildrenrendered
35005         * Fires right before the child nodes for this node are rendered
35006         * @param {Node} this This node
35007         */
35008         "beforechildrenrendered":true
35009     });
35010
35011     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35012
35013     /**
35014      * Read-only. The UI for this node
35015      * @type TreeNodeUI
35016      */
35017     this.ui = new uiClass(this);
35018     
35019     // finally support items[]
35020     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35021         return;
35022     }
35023     
35024     
35025     Roo.each(this.attributes.items, function(c) {
35026         this.appendChild(Roo.factory(c,Roo.Tree));
35027     }, this);
35028     delete this.attributes.items;
35029     
35030     
35031     
35032 };
35033 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35034     preventHScroll: true,
35035     /**
35036      * Returns true if this node is expanded
35037      * @return {Boolean}
35038      */
35039     isExpanded : function(){
35040         return this.expanded;
35041     },
35042
35043     /**
35044      * Returns the UI object for this node
35045      * @return {TreeNodeUI}
35046      */
35047     getUI : function(){
35048         return this.ui;
35049     },
35050
35051     // private override
35052     setFirstChild : function(node){
35053         var of = this.firstChild;
35054         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35055         if(this.childrenRendered && of && node != of){
35056             of.renderIndent(true, true);
35057         }
35058         if(this.rendered){
35059             this.renderIndent(true, true);
35060         }
35061     },
35062
35063     // private override
35064     setLastChild : function(node){
35065         var ol = this.lastChild;
35066         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35067         if(this.childrenRendered && ol && node != ol){
35068             ol.renderIndent(true, true);
35069         }
35070         if(this.rendered){
35071             this.renderIndent(true, true);
35072         }
35073     },
35074
35075     // these methods are overridden to provide lazy rendering support
35076     // private override
35077     appendChild : function()
35078     {
35079         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35080         if(node && this.childrenRendered){
35081             node.render();
35082         }
35083         this.ui.updateExpandIcon();
35084         return node;
35085     },
35086
35087     // private override
35088     removeChild : function(node){
35089         this.ownerTree.getSelectionModel().unselect(node);
35090         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35091         // if it's been rendered remove dom node
35092         if(this.childrenRendered){
35093             node.ui.remove();
35094         }
35095         if(this.childNodes.length < 1){
35096             this.collapse(false, false);
35097         }else{
35098             this.ui.updateExpandIcon();
35099         }
35100         if(!this.firstChild) {
35101             this.childrenRendered = false;
35102         }
35103         return node;
35104     },
35105
35106     // private override
35107     insertBefore : function(node, refNode){
35108         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35109         if(newNode && refNode && this.childrenRendered){
35110             node.render();
35111         }
35112         this.ui.updateExpandIcon();
35113         return newNode;
35114     },
35115
35116     /**
35117      * Sets the text for this node
35118      * @param {String} text
35119      */
35120     setText : function(text){
35121         var oldText = this.text;
35122         this.text = text;
35123         this.attributes.text = text;
35124         if(this.rendered){ // event without subscribing
35125             this.ui.onTextChange(this, text, oldText);
35126         }
35127         this.fireEvent("textchange", this, text, oldText);
35128     },
35129
35130     /**
35131      * Triggers selection of this node
35132      */
35133     select : function(){
35134         this.getOwnerTree().getSelectionModel().select(this);
35135     },
35136
35137     /**
35138      * Triggers deselection of this node
35139      */
35140     unselect : function(){
35141         this.getOwnerTree().getSelectionModel().unselect(this);
35142     },
35143
35144     /**
35145      * Returns true if this node is selected
35146      * @return {Boolean}
35147      */
35148     isSelected : function(){
35149         return this.getOwnerTree().getSelectionModel().isSelected(this);
35150     },
35151
35152     /**
35153      * Expand this node.
35154      * @param {Boolean} deep (optional) True to expand all children as well
35155      * @param {Boolean} anim (optional) false to cancel the default animation
35156      * @param {Function} callback (optional) A callback to be called when
35157      * expanding this node completes (does not wait for deep expand to complete).
35158      * Called with 1 parameter, this node.
35159      */
35160     expand : function(deep, anim, callback){
35161         if(!this.expanded){
35162             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35163                 return;
35164             }
35165             if(!this.childrenRendered){
35166                 this.renderChildren();
35167             }
35168             this.expanded = true;
35169             
35170             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35171                 this.ui.animExpand(function(){
35172                     this.fireEvent("expand", this);
35173                     if(typeof callback == "function"){
35174                         callback(this);
35175                     }
35176                     if(deep === true){
35177                         this.expandChildNodes(true);
35178                     }
35179                 }.createDelegate(this));
35180                 return;
35181             }else{
35182                 this.ui.expand();
35183                 this.fireEvent("expand", this);
35184                 if(typeof callback == "function"){
35185                     callback(this);
35186                 }
35187             }
35188         }else{
35189            if(typeof callback == "function"){
35190                callback(this);
35191            }
35192         }
35193         if(deep === true){
35194             this.expandChildNodes(true);
35195         }
35196     },
35197
35198     isHiddenRoot : function(){
35199         return this.isRoot && !this.getOwnerTree().rootVisible;
35200     },
35201
35202     /**
35203      * Collapse this node.
35204      * @param {Boolean} deep (optional) True to collapse all children as well
35205      * @param {Boolean} anim (optional) false to cancel the default animation
35206      */
35207     collapse : function(deep, anim){
35208         if(this.expanded && !this.isHiddenRoot()){
35209             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35210                 return;
35211             }
35212             this.expanded = false;
35213             if((this.getOwnerTree().animate && anim !== false) || anim){
35214                 this.ui.animCollapse(function(){
35215                     this.fireEvent("collapse", this);
35216                     if(deep === true){
35217                         this.collapseChildNodes(true);
35218                     }
35219                 }.createDelegate(this));
35220                 return;
35221             }else{
35222                 this.ui.collapse();
35223                 this.fireEvent("collapse", this);
35224             }
35225         }
35226         if(deep === true){
35227             var cs = this.childNodes;
35228             for(var i = 0, len = cs.length; i < len; i++) {
35229                 cs[i].collapse(true, false);
35230             }
35231         }
35232     },
35233
35234     // private
35235     delayedExpand : function(delay){
35236         if(!this.expandProcId){
35237             this.expandProcId = this.expand.defer(delay, this);
35238         }
35239     },
35240
35241     // private
35242     cancelExpand : function(){
35243         if(this.expandProcId){
35244             clearTimeout(this.expandProcId);
35245         }
35246         this.expandProcId = false;
35247     },
35248
35249     /**
35250      * Toggles expanded/collapsed state of the node
35251      */
35252     toggle : function(){
35253         if(this.expanded){
35254             this.collapse();
35255         }else{
35256             this.expand();
35257         }
35258     },
35259
35260     /**
35261      * Ensures all parent nodes are expanded
35262      */
35263     ensureVisible : function(callback){
35264         var tree = this.getOwnerTree();
35265         tree.expandPath(this.parentNode.getPath(), false, function(){
35266             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35267             Roo.callback(callback);
35268         }.createDelegate(this));
35269     },
35270
35271     /**
35272      * Expand all child nodes
35273      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35274      */
35275     expandChildNodes : function(deep){
35276         var cs = this.childNodes;
35277         for(var i = 0, len = cs.length; i < len; i++) {
35278                 cs[i].expand(deep);
35279         }
35280     },
35281
35282     /**
35283      * Collapse all child nodes
35284      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35285      */
35286     collapseChildNodes : function(deep){
35287         var cs = this.childNodes;
35288         for(var i = 0, len = cs.length; i < len; i++) {
35289                 cs[i].collapse(deep);
35290         }
35291     },
35292
35293     /**
35294      * Disables this node
35295      */
35296     disable : function(){
35297         this.disabled = true;
35298         this.unselect();
35299         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35300             this.ui.onDisableChange(this, true);
35301         }
35302         this.fireEvent("disabledchange", this, true);
35303     },
35304
35305     /**
35306      * Enables this node
35307      */
35308     enable : function(){
35309         this.disabled = false;
35310         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35311             this.ui.onDisableChange(this, false);
35312         }
35313         this.fireEvent("disabledchange", this, false);
35314     },
35315
35316     // private
35317     renderChildren : function(suppressEvent){
35318         if(suppressEvent !== false){
35319             this.fireEvent("beforechildrenrendered", this);
35320         }
35321         var cs = this.childNodes;
35322         for(var i = 0, len = cs.length; i < len; i++){
35323             cs[i].render(true);
35324         }
35325         this.childrenRendered = true;
35326     },
35327
35328     // private
35329     sort : function(fn, scope){
35330         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35331         if(this.childrenRendered){
35332             var cs = this.childNodes;
35333             for(var i = 0, len = cs.length; i < len; i++){
35334                 cs[i].render(true);
35335             }
35336         }
35337     },
35338
35339     // private
35340     render : function(bulkRender){
35341         this.ui.render(bulkRender);
35342         if(!this.rendered){
35343             this.rendered = true;
35344             if(this.expanded){
35345                 this.expanded = false;
35346                 this.expand(false, false);
35347             }
35348         }
35349     },
35350
35351     // private
35352     renderIndent : function(deep, refresh){
35353         if(refresh){
35354             this.ui.childIndent = null;
35355         }
35356         this.ui.renderIndent();
35357         if(deep === true && this.childrenRendered){
35358             var cs = this.childNodes;
35359             for(var i = 0, len = cs.length; i < len; i++){
35360                 cs[i].renderIndent(true, refresh);
35361             }
35362         }
35363     }
35364 });/*
35365  * Based on:
35366  * Ext JS Library 1.1.1
35367  * Copyright(c) 2006-2007, Ext JS, LLC.
35368  *
35369  * Originally Released Under LGPL - original licence link has changed is not relivant.
35370  *
35371  * Fork - LGPL
35372  * <script type="text/javascript">
35373  */
35374  
35375 /**
35376  * @class Roo.tree.AsyncTreeNode
35377  * @extends Roo.tree.TreeNode
35378  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35379  * @constructor
35380  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35381  */
35382  Roo.tree.AsyncTreeNode = function(config){
35383     this.loaded = false;
35384     this.loading = false;
35385     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35386     /**
35387     * @event beforeload
35388     * Fires before this node is loaded, return false to cancel
35389     * @param {Node} this This node
35390     */
35391     this.addEvents({'beforeload':true, 'load': true});
35392     /**
35393     * @event load
35394     * Fires when this node is loaded
35395     * @param {Node} this This node
35396     */
35397     /**
35398      * The loader used by this node (defaults to using the tree's defined loader)
35399      * @type TreeLoader
35400      * @property loader
35401      */
35402 };
35403 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35404     expand : function(deep, anim, callback){
35405         if(this.loading){ // if an async load is already running, waiting til it's done
35406             var timer;
35407             var f = function(){
35408                 if(!this.loading){ // done loading
35409                     clearInterval(timer);
35410                     this.expand(deep, anim, callback);
35411                 }
35412             }.createDelegate(this);
35413             timer = setInterval(f, 200);
35414             return;
35415         }
35416         if(!this.loaded){
35417             if(this.fireEvent("beforeload", this) === false){
35418                 return;
35419             }
35420             this.loading = true;
35421             this.ui.beforeLoad(this);
35422             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35423             if(loader){
35424                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35425                 return;
35426             }
35427         }
35428         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35429     },
35430     
35431     /**
35432      * Returns true if this node is currently loading
35433      * @return {Boolean}
35434      */
35435     isLoading : function(){
35436         return this.loading;  
35437     },
35438     
35439     loadComplete : function(deep, anim, callback){
35440         this.loading = false;
35441         this.loaded = true;
35442         this.ui.afterLoad(this);
35443         this.fireEvent("load", this);
35444         this.expand(deep, anim, callback);
35445     },
35446     
35447     /**
35448      * Returns true if this node has been loaded
35449      * @return {Boolean}
35450      */
35451     isLoaded : function(){
35452         return this.loaded;
35453     },
35454     
35455     hasChildNodes : function(){
35456         if(!this.isLeaf() && !this.loaded){
35457             return true;
35458         }else{
35459             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35460         }
35461     },
35462
35463     /**
35464      * Trigger a reload for this node
35465      * @param {Function} callback
35466      */
35467     reload : function(callback){
35468         this.collapse(false, false);
35469         while(this.firstChild){
35470             this.removeChild(this.firstChild);
35471         }
35472         this.childrenRendered = false;
35473         this.loaded = false;
35474         if(this.isHiddenRoot()){
35475             this.expanded = false;
35476         }
35477         this.expand(false, false, callback);
35478     }
35479 });/*
35480  * Based on:
35481  * Ext JS Library 1.1.1
35482  * Copyright(c) 2006-2007, Ext JS, LLC.
35483  *
35484  * Originally Released Under LGPL - original licence link has changed is not relivant.
35485  *
35486  * Fork - LGPL
35487  * <script type="text/javascript">
35488  */
35489  
35490 /**
35491  * @class Roo.tree.TreeNodeUI
35492  * @constructor
35493  * @param {Object} node The node to render
35494  * The TreeNode UI implementation is separate from the
35495  * tree implementation. Unless you are customizing the tree UI,
35496  * you should never have to use this directly.
35497  */
35498 Roo.tree.TreeNodeUI = function(node){
35499     this.node = node;
35500     this.rendered = false;
35501     this.animating = false;
35502     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35503 };
35504
35505 Roo.tree.TreeNodeUI.prototype = {
35506     removeChild : function(node){
35507         if(this.rendered){
35508             this.ctNode.removeChild(node.ui.getEl());
35509         }
35510     },
35511
35512     beforeLoad : function(){
35513          this.addClass("x-tree-node-loading");
35514     },
35515
35516     afterLoad : function(){
35517          this.removeClass("x-tree-node-loading");
35518     },
35519
35520     onTextChange : function(node, text, oldText){
35521         if(this.rendered){
35522             this.textNode.innerHTML = text;
35523         }
35524     },
35525
35526     onDisableChange : function(node, state){
35527         this.disabled = state;
35528         if(state){
35529             this.addClass("x-tree-node-disabled");
35530         }else{
35531             this.removeClass("x-tree-node-disabled");
35532         }
35533     },
35534
35535     onSelectedChange : function(state){
35536         if(state){
35537             this.focus();
35538             this.addClass("x-tree-selected");
35539         }else{
35540             //this.blur();
35541             this.removeClass("x-tree-selected");
35542         }
35543     },
35544
35545     onMove : function(tree, node, oldParent, newParent, index, refNode){
35546         this.childIndent = null;
35547         if(this.rendered){
35548             var targetNode = newParent.ui.getContainer();
35549             if(!targetNode){//target not rendered
35550                 this.holder = document.createElement("div");
35551                 this.holder.appendChild(this.wrap);
35552                 return;
35553             }
35554             var insertBefore = refNode ? refNode.ui.getEl() : null;
35555             if(insertBefore){
35556                 targetNode.insertBefore(this.wrap, insertBefore);
35557             }else{
35558                 targetNode.appendChild(this.wrap);
35559             }
35560             this.node.renderIndent(true);
35561         }
35562     },
35563
35564     addClass : function(cls){
35565         if(this.elNode){
35566             Roo.fly(this.elNode).addClass(cls);
35567         }
35568     },
35569
35570     removeClass : function(cls){
35571         if(this.elNode){
35572             Roo.fly(this.elNode).removeClass(cls);
35573         }
35574     },
35575
35576     remove : function(){
35577         if(this.rendered){
35578             this.holder = document.createElement("div");
35579             this.holder.appendChild(this.wrap);
35580         }
35581     },
35582
35583     fireEvent : function(){
35584         return this.node.fireEvent.apply(this.node, arguments);
35585     },
35586
35587     initEvents : function(){
35588         this.node.on("move", this.onMove, this);
35589         var E = Roo.EventManager;
35590         var a = this.anchor;
35591
35592         var el = Roo.fly(a, '_treeui');
35593
35594         if(Roo.isOpera){ // opera render bug ignores the CSS
35595             el.setStyle("text-decoration", "none");
35596         }
35597
35598         el.on("click", this.onClick, this);
35599         el.on("dblclick", this.onDblClick, this);
35600
35601         if(this.checkbox){
35602             Roo.EventManager.on(this.checkbox,
35603                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35604         }
35605
35606         el.on("contextmenu", this.onContextMenu, this);
35607
35608         var icon = Roo.fly(this.iconNode);
35609         icon.on("click", this.onClick, this);
35610         icon.on("dblclick", this.onDblClick, this);
35611         icon.on("contextmenu", this.onContextMenu, this);
35612         E.on(this.ecNode, "click", this.ecClick, this, true);
35613
35614         if(this.node.disabled){
35615             this.addClass("x-tree-node-disabled");
35616         }
35617         if(this.node.hidden){
35618             this.addClass("x-tree-node-disabled");
35619         }
35620         var ot = this.node.getOwnerTree();
35621         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35622         if(dd && (!this.node.isRoot || ot.rootVisible)){
35623             Roo.dd.Registry.register(this.elNode, {
35624                 node: this.node,
35625                 handles: this.getDDHandles(),
35626                 isHandle: false
35627             });
35628         }
35629     },
35630
35631     getDDHandles : function(){
35632         return [this.iconNode, this.textNode];
35633     },
35634
35635     hide : function(){
35636         if(this.rendered){
35637             this.wrap.style.display = "none";
35638         }
35639     },
35640
35641     show : function(){
35642         if(this.rendered){
35643             this.wrap.style.display = "";
35644         }
35645     },
35646
35647     onContextMenu : function(e){
35648         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35649             e.preventDefault();
35650             this.focus();
35651             this.fireEvent("contextmenu", this.node, e);
35652         }
35653     },
35654
35655     onClick : function(e){
35656         if(this.dropping){
35657             e.stopEvent();
35658             return;
35659         }
35660         if(this.fireEvent("beforeclick", this.node, e) !== false){
35661             if(!this.disabled && this.node.attributes.href){
35662                 this.fireEvent("click", this.node, e);
35663                 return;
35664             }
35665             e.preventDefault();
35666             if(this.disabled){
35667                 return;
35668             }
35669
35670             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35671                 this.node.toggle();
35672             }
35673
35674             this.fireEvent("click", this.node, e);
35675         }else{
35676             e.stopEvent();
35677         }
35678     },
35679
35680     onDblClick : function(e){
35681         e.preventDefault();
35682         if(this.disabled){
35683             return;
35684         }
35685         if(this.checkbox){
35686             this.toggleCheck();
35687         }
35688         if(!this.animating && this.node.hasChildNodes()){
35689             this.node.toggle();
35690         }
35691         this.fireEvent("dblclick", this.node, e);
35692     },
35693
35694     onCheckChange : function(){
35695         var checked = this.checkbox.checked;
35696         this.node.attributes.checked = checked;
35697         this.fireEvent('checkchange', this.node, checked);
35698     },
35699
35700     ecClick : function(e){
35701         if(!this.animating && this.node.hasChildNodes()){
35702             this.node.toggle();
35703         }
35704     },
35705
35706     startDrop : function(){
35707         this.dropping = true;
35708     },
35709
35710     // delayed drop so the click event doesn't get fired on a drop
35711     endDrop : function(){
35712        setTimeout(function(){
35713            this.dropping = false;
35714        }.createDelegate(this), 50);
35715     },
35716
35717     expand : function(){
35718         this.updateExpandIcon();
35719         this.ctNode.style.display = "";
35720     },
35721
35722     focus : function(){
35723         if(!this.node.preventHScroll){
35724             try{this.anchor.focus();
35725             }catch(e){}
35726         }else if(!Roo.isIE){
35727             try{
35728                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35729                 var l = noscroll.scrollLeft;
35730                 this.anchor.focus();
35731                 noscroll.scrollLeft = l;
35732             }catch(e){}
35733         }
35734     },
35735
35736     toggleCheck : function(value){
35737         var cb = this.checkbox;
35738         if(cb){
35739             cb.checked = (value === undefined ? !cb.checked : value);
35740         }
35741     },
35742
35743     blur : function(){
35744         try{
35745             this.anchor.blur();
35746         }catch(e){}
35747     },
35748
35749     animExpand : function(callback){
35750         var ct = Roo.get(this.ctNode);
35751         ct.stopFx();
35752         if(!this.node.hasChildNodes()){
35753             this.updateExpandIcon();
35754             this.ctNode.style.display = "";
35755             Roo.callback(callback);
35756             return;
35757         }
35758         this.animating = true;
35759         this.updateExpandIcon();
35760
35761         ct.slideIn('t', {
35762            callback : function(){
35763                this.animating = false;
35764                Roo.callback(callback);
35765             },
35766             scope: this,
35767             duration: this.node.ownerTree.duration || .25
35768         });
35769     },
35770
35771     highlight : function(){
35772         var tree = this.node.getOwnerTree();
35773         Roo.fly(this.wrap).highlight(
35774             tree.hlColor || "C3DAF9",
35775             {endColor: tree.hlBaseColor}
35776         );
35777     },
35778
35779     collapse : function(){
35780         this.updateExpandIcon();
35781         this.ctNode.style.display = "none";
35782     },
35783
35784     animCollapse : function(callback){
35785         var ct = Roo.get(this.ctNode);
35786         ct.enableDisplayMode('block');
35787         ct.stopFx();
35788
35789         this.animating = true;
35790         this.updateExpandIcon();
35791
35792         ct.slideOut('t', {
35793             callback : function(){
35794                this.animating = false;
35795                Roo.callback(callback);
35796             },
35797             scope: this,
35798             duration: this.node.ownerTree.duration || .25
35799         });
35800     },
35801
35802     getContainer : function(){
35803         return this.ctNode;
35804     },
35805
35806     getEl : function(){
35807         return this.wrap;
35808     },
35809
35810     appendDDGhost : function(ghostNode){
35811         ghostNode.appendChild(this.elNode.cloneNode(true));
35812     },
35813
35814     getDDRepairXY : function(){
35815         return Roo.lib.Dom.getXY(this.iconNode);
35816     },
35817
35818     onRender : function(){
35819         this.render();
35820     },
35821
35822     render : function(bulkRender){
35823         var n = this.node, a = n.attributes;
35824         var targetNode = n.parentNode ?
35825               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35826
35827         if(!this.rendered){
35828             this.rendered = true;
35829
35830             this.renderElements(n, a, targetNode, bulkRender);
35831
35832             if(a.qtip){
35833                if(this.textNode.setAttributeNS){
35834                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35835                    if(a.qtipTitle){
35836                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35837                    }
35838                }else{
35839                    this.textNode.setAttribute("ext:qtip", a.qtip);
35840                    if(a.qtipTitle){
35841                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35842                    }
35843                }
35844             }else if(a.qtipCfg){
35845                 a.qtipCfg.target = Roo.id(this.textNode);
35846                 Roo.QuickTips.register(a.qtipCfg);
35847             }
35848             this.initEvents();
35849             if(!this.node.expanded){
35850                 this.updateExpandIcon();
35851             }
35852         }else{
35853             if(bulkRender === true) {
35854                 targetNode.appendChild(this.wrap);
35855             }
35856         }
35857     },
35858
35859     renderElements : function(n, a, targetNode, bulkRender)
35860     {
35861         // add some indent caching, this helps performance when rendering a large tree
35862         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35863         var t = n.getOwnerTree();
35864         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35865         if (typeof(n.attributes.html) != 'undefined') {
35866             txt = n.attributes.html;
35867         }
35868         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
35869         var cb = typeof a.checked == 'boolean';
35870         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35871         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35872             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35873             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35874             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35875             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35876             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35877              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35878                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35879             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35880             "</li>"];
35881
35882         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35883             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35884                                 n.nextSibling.ui.getEl(), buf.join(""));
35885         }else{
35886             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35887         }
35888
35889         this.elNode = this.wrap.childNodes[0];
35890         this.ctNode = this.wrap.childNodes[1];
35891         var cs = this.elNode.childNodes;
35892         this.indentNode = cs[0];
35893         this.ecNode = cs[1];
35894         this.iconNode = cs[2];
35895         var index = 3;
35896         if(cb){
35897             this.checkbox = cs[3];
35898             index++;
35899         }
35900         this.anchor = cs[index];
35901         this.textNode = cs[index].firstChild;
35902     },
35903
35904     getAnchor : function(){
35905         return this.anchor;
35906     },
35907
35908     getTextEl : function(){
35909         return this.textNode;
35910     },
35911
35912     getIconEl : function(){
35913         return this.iconNode;
35914     },
35915
35916     isChecked : function(){
35917         return this.checkbox ? this.checkbox.checked : false;
35918     },
35919
35920     updateExpandIcon : function(){
35921         if(this.rendered){
35922             var n = this.node, c1, c2;
35923             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35924             var hasChild = n.hasChildNodes();
35925             if(hasChild){
35926                 if(n.expanded){
35927                     cls += "-minus";
35928                     c1 = "x-tree-node-collapsed";
35929                     c2 = "x-tree-node-expanded";
35930                 }else{
35931                     cls += "-plus";
35932                     c1 = "x-tree-node-expanded";
35933                     c2 = "x-tree-node-collapsed";
35934                 }
35935                 if(this.wasLeaf){
35936                     this.removeClass("x-tree-node-leaf");
35937                     this.wasLeaf = false;
35938                 }
35939                 if(this.c1 != c1 || this.c2 != c2){
35940                     Roo.fly(this.elNode).replaceClass(c1, c2);
35941                     this.c1 = c1; this.c2 = c2;
35942                 }
35943             }else{
35944                 // this changes non-leafs into leafs if they have no children.
35945                 // it's not very rational behaviour..
35946                 
35947                 if(!this.wasLeaf && this.node.leaf){
35948                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35949                     delete this.c1;
35950                     delete this.c2;
35951                     this.wasLeaf = true;
35952                 }
35953             }
35954             var ecc = "x-tree-ec-icon "+cls;
35955             if(this.ecc != ecc){
35956                 this.ecNode.className = ecc;
35957                 this.ecc = ecc;
35958             }
35959         }
35960     },
35961
35962     getChildIndent : function(){
35963         if(!this.childIndent){
35964             var buf = [];
35965             var p = this.node;
35966             while(p){
35967                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35968                     if(!p.isLast()) {
35969                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35970                     } else {
35971                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35972                     }
35973                 }
35974                 p = p.parentNode;
35975             }
35976             this.childIndent = buf.join("");
35977         }
35978         return this.childIndent;
35979     },
35980
35981     renderIndent : function(){
35982         if(this.rendered){
35983             var indent = "";
35984             var p = this.node.parentNode;
35985             if(p){
35986                 indent = p.ui.getChildIndent();
35987             }
35988             if(this.indentMarkup != indent){ // don't rerender if not required
35989                 this.indentNode.innerHTML = indent;
35990                 this.indentMarkup = indent;
35991             }
35992             this.updateExpandIcon();
35993         }
35994     }
35995 };
35996
35997 Roo.tree.RootTreeNodeUI = function(){
35998     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35999 };
36000 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
36001     render : function(){
36002         if(!this.rendered){
36003             var targetNode = this.node.ownerTree.innerCt.dom;
36004             this.node.expanded = true;
36005             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36006             this.wrap = this.ctNode = targetNode.firstChild;
36007         }
36008     },
36009     collapse : function(){
36010     },
36011     expand : function(){
36012     }
36013 });/*
36014  * Based on:
36015  * Ext JS Library 1.1.1
36016  * Copyright(c) 2006-2007, Ext JS, LLC.
36017  *
36018  * Originally Released Under LGPL - original licence link has changed is not relivant.
36019  *
36020  * Fork - LGPL
36021  * <script type="text/javascript">
36022  */
36023 /**
36024  * @class Roo.tree.TreeLoader
36025  * @extends Roo.util.Observable
36026  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36027  * nodes from a specified URL. The response must be a javascript Array definition
36028  * who's elements are node definition objects. eg:
36029  * <pre><code>
36030 {  success : true,
36031    data :      [
36032    
36033     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36034     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36035     ]
36036 }
36037
36038
36039 </code></pre>
36040  * <br><br>
36041  * The old style respose with just an array is still supported, but not recommended.
36042  * <br><br>
36043  *
36044  * A server request is sent, and child nodes are loaded only when a node is expanded.
36045  * The loading node's id is passed to the server under the parameter name "node" to
36046  * enable the server to produce the correct child nodes.
36047  * <br><br>
36048  * To pass extra parameters, an event handler may be attached to the "beforeload"
36049  * event, and the parameters specified in the TreeLoader's baseParams property:
36050  * <pre><code>
36051     myTreeLoader.on("beforeload", function(treeLoader, node) {
36052         this.baseParams.category = node.attributes.category;
36053     }, this);
36054     
36055 </code></pre>
36056  *
36057  * This would pass an HTTP parameter called "category" to the server containing
36058  * the value of the Node's "category" attribute.
36059  * @constructor
36060  * Creates a new Treeloader.
36061  * @param {Object} config A config object containing config properties.
36062  */
36063 Roo.tree.TreeLoader = function(config){
36064     this.baseParams = {};
36065     this.requestMethod = "POST";
36066     Roo.apply(this, config);
36067
36068     this.addEvents({
36069     
36070         /**
36071          * @event beforeload
36072          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36073          * @param {Object} This TreeLoader object.
36074          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36075          * @param {Object} callback The callback function specified in the {@link #load} call.
36076          */
36077         beforeload : true,
36078         /**
36079          * @event load
36080          * Fires when the node has been successfuly loaded.
36081          * @param {Object} This TreeLoader object.
36082          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36083          * @param {Object} response The response object containing the data from the server.
36084          */
36085         load : true,
36086         /**
36087          * @event loadexception
36088          * Fires if the network request failed.
36089          * @param {Object} This TreeLoader object.
36090          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36091          * @param {Object} response The response object containing the data from the server.
36092          */
36093         loadexception : true,
36094         /**
36095          * @event create
36096          * Fires before a node is created, enabling you to return custom Node types 
36097          * @param {Object} This TreeLoader object.
36098          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36099          */
36100         create : true
36101     });
36102
36103     Roo.tree.TreeLoader.superclass.constructor.call(this);
36104 };
36105
36106 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36107     /**
36108     * @cfg {String} dataUrl The URL from which to request a Json string which
36109     * specifies an array of node definition object representing the child nodes
36110     * to be loaded.
36111     */
36112     /**
36113     * @cfg {String} requestMethod either GET or POST
36114     * defaults to POST (due to BC)
36115     * to be loaded.
36116     */
36117     /**
36118     * @cfg {Object} baseParams (optional) An object containing properties which
36119     * specify HTTP parameters to be passed to each request for child nodes.
36120     */
36121     /**
36122     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36123     * created by this loader. If the attributes sent by the server have an attribute in this object,
36124     * they take priority.
36125     */
36126     /**
36127     * @cfg {Object} uiProviders (optional) An object containing properties which
36128     * 
36129     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36130     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36131     * <i>uiProvider</i> attribute of a returned child node is a string rather
36132     * than a reference to a TreeNodeUI implementation, this that string value
36133     * is used as a property name in the uiProviders object. You can define the provider named
36134     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36135     */
36136     uiProviders : {},
36137
36138     /**
36139     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36140     * child nodes before loading.
36141     */
36142     clearOnLoad : true,
36143
36144     /**
36145     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36146     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36147     * Grid query { data : [ .....] }
36148     */
36149     
36150     root : false,
36151      /**
36152     * @cfg {String} queryParam (optional) 
36153     * Name of the query as it will be passed on the querystring (defaults to 'node')
36154     * eg. the request will be ?node=[id]
36155     */
36156     
36157     
36158     queryParam: false,
36159     
36160     /**
36161      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36162      * This is called automatically when a node is expanded, but may be used to reload
36163      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36164      * @param {Roo.tree.TreeNode} node
36165      * @param {Function} callback
36166      */
36167     load : function(node, callback){
36168         if(this.clearOnLoad){
36169             while(node.firstChild){
36170                 node.removeChild(node.firstChild);
36171             }
36172         }
36173         if(node.attributes.children){ // preloaded json children
36174             var cs = node.attributes.children;
36175             for(var i = 0, len = cs.length; i < len; i++){
36176                 node.appendChild(this.createNode(cs[i]));
36177             }
36178             if(typeof callback == "function"){
36179                 callback();
36180             }
36181         }else if(this.dataUrl){
36182             this.requestData(node, callback);
36183         }
36184     },
36185
36186     getParams: function(node){
36187         var buf = [], bp = this.baseParams;
36188         for(var key in bp){
36189             if(typeof bp[key] != "function"){
36190                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36191             }
36192         }
36193         var n = this.queryParam === false ? 'node' : this.queryParam;
36194         buf.push(n + "=", encodeURIComponent(node.id));
36195         return buf.join("");
36196     },
36197
36198     requestData : function(node, callback){
36199         if(this.fireEvent("beforeload", this, node, callback) !== false){
36200             this.transId = Roo.Ajax.request({
36201                 method:this.requestMethod,
36202                 url: this.dataUrl||this.url,
36203                 success: this.handleResponse,
36204                 failure: this.handleFailure,
36205                 scope: this,
36206                 argument: {callback: callback, node: node},
36207                 params: this.getParams(node)
36208             });
36209         }else{
36210             // if the load is cancelled, make sure we notify
36211             // the node that we are done
36212             if(typeof callback == "function"){
36213                 callback();
36214             }
36215         }
36216     },
36217
36218     isLoading : function(){
36219         return this.transId ? true : false;
36220     },
36221
36222     abort : function(){
36223         if(this.isLoading()){
36224             Roo.Ajax.abort(this.transId);
36225         }
36226     },
36227
36228     // private
36229     createNode : function(attr)
36230     {
36231         // apply baseAttrs, nice idea Corey!
36232         if(this.baseAttrs){
36233             Roo.applyIf(attr, this.baseAttrs);
36234         }
36235         if(this.applyLoader !== false){
36236             attr.loader = this;
36237         }
36238         // uiProvider = depreciated..
36239         
36240         if(typeof(attr.uiProvider) == 'string'){
36241            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36242                 /**  eval:var:attr */ eval(attr.uiProvider);
36243         }
36244         if(typeof(this.uiProviders['default']) != 'undefined') {
36245             attr.uiProvider = this.uiProviders['default'];
36246         }
36247         
36248         this.fireEvent('create', this, attr);
36249         
36250         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36251         return(attr.leaf ?
36252                         new Roo.tree.TreeNode(attr) :
36253                         new Roo.tree.AsyncTreeNode(attr));
36254     },
36255
36256     processResponse : function(response, node, callback)
36257     {
36258         var json = response.responseText;
36259         try {
36260             
36261             var o = Roo.decode(json);
36262             
36263             if (this.root === false && typeof(o.success) != undefined) {
36264                 this.root = 'data'; // the default behaviour for list like data..
36265                 }
36266                 
36267             if (this.root !== false &&  !o.success) {
36268                 // it's a failure condition.
36269                 var a = response.argument;
36270                 this.fireEvent("loadexception", this, a.node, response);
36271                 Roo.log("Load failed - should have a handler really");
36272                 return;
36273             }
36274             
36275             
36276             
36277             if (this.root !== false) {
36278                  o = o[this.root];
36279             }
36280             
36281             for(var i = 0, len = o.length; i < len; i++){
36282                 var n = this.createNode(o[i]);
36283                 if(n){
36284                     node.appendChild(n);
36285                 }
36286             }
36287             if(typeof callback == "function"){
36288                 callback(this, node);
36289             }
36290         }catch(e){
36291             this.handleFailure(response);
36292         }
36293     },
36294
36295     handleResponse : function(response){
36296         this.transId = false;
36297         var a = response.argument;
36298         this.processResponse(response, a.node, a.callback);
36299         this.fireEvent("load", this, a.node, response);
36300     },
36301
36302     handleFailure : function(response)
36303     {
36304         // should handle failure better..
36305         this.transId = false;
36306         var a = response.argument;
36307         this.fireEvent("loadexception", this, a.node, response);
36308         if(typeof a.callback == "function"){
36309             a.callback(this, a.node);
36310         }
36311     }
36312 });/*
36313  * Based on:
36314  * Ext JS Library 1.1.1
36315  * Copyright(c) 2006-2007, Ext JS, LLC.
36316  *
36317  * Originally Released Under LGPL - original licence link has changed is not relivant.
36318  *
36319  * Fork - LGPL
36320  * <script type="text/javascript">
36321  */
36322
36323 /**
36324 * @class Roo.tree.TreeFilter
36325 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36326 * @param {TreePanel} tree
36327 * @param {Object} config (optional)
36328  */
36329 Roo.tree.TreeFilter = function(tree, config){
36330     this.tree = tree;
36331     this.filtered = {};
36332     Roo.apply(this, config);
36333 };
36334
36335 Roo.tree.TreeFilter.prototype = {
36336     clearBlank:false,
36337     reverse:false,
36338     autoClear:false,
36339     remove:false,
36340
36341      /**
36342      * Filter the data by a specific attribute.
36343      * @param {String/RegExp} value Either string that the attribute value
36344      * should start with or a RegExp to test against the attribute
36345      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36346      * @param {TreeNode} startNode (optional) The node to start the filter at.
36347      */
36348     filter : function(value, attr, startNode){
36349         attr = attr || "text";
36350         var f;
36351         if(typeof value == "string"){
36352             var vlen = value.length;
36353             // auto clear empty filter
36354             if(vlen == 0 && this.clearBlank){
36355                 this.clear();
36356                 return;
36357             }
36358             value = value.toLowerCase();
36359             f = function(n){
36360                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36361             };
36362         }else if(value.exec){ // regex?
36363             f = function(n){
36364                 return value.test(n.attributes[attr]);
36365             };
36366         }else{
36367             throw 'Illegal filter type, must be string or regex';
36368         }
36369         this.filterBy(f, null, startNode);
36370         },
36371
36372     /**
36373      * Filter by a function. The passed function will be called with each
36374      * node in the tree (or from the startNode). If the function returns true, the node is kept
36375      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36376      * @param {Function} fn The filter function
36377      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36378      */
36379     filterBy : function(fn, scope, startNode){
36380         startNode = startNode || this.tree.root;
36381         if(this.autoClear){
36382             this.clear();
36383         }
36384         var af = this.filtered, rv = this.reverse;
36385         var f = function(n){
36386             if(n == startNode){
36387                 return true;
36388             }
36389             if(af[n.id]){
36390                 return false;
36391             }
36392             var m = fn.call(scope || n, n);
36393             if(!m || rv){
36394                 af[n.id] = n;
36395                 n.ui.hide();
36396                 return false;
36397             }
36398             return true;
36399         };
36400         startNode.cascade(f);
36401         if(this.remove){
36402            for(var id in af){
36403                if(typeof id != "function"){
36404                    var n = af[id];
36405                    if(n && n.parentNode){
36406                        n.parentNode.removeChild(n);
36407                    }
36408                }
36409            }
36410         }
36411     },
36412
36413     /**
36414      * Clears the current filter. Note: with the "remove" option
36415      * set a filter cannot be cleared.
36416      */
36417     clear : function(){
36418         var t = this.tree;
36419         var af = this.filtered;
36420         for(var id in af){
36421             if(typeof id != "function"){
36422                 var n = af[id];
36423                 if(n){
36424                     n.ui.show();
36425                 }
36426             }
36427         }
36428         this.filtered = {};
36429     }
36430 };
36431 /*
36432  * Based on:
36433  * Ext JS Library 1.1.1
36434  * Copyright(c) 2006-2007, Ext JS, LLC.
36435  *
36436  * Originally Released Under LGPL - original licence link has changed is not relivant.
36437  *
36438  * Fork - LGPL
36439  * <script type="text/javascript">
36440  */
36441  
36442
36443 /**
36444  * @class Roo.tree.TreeSorter
36445  * Provides sorting of nodes in a TreePanel
36446  * 
36447  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36448  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36449  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36450  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36451  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36452  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36453  * @constructor
36454  * @param {TreePanel} tree
36455  * @param {Object} config
36456  */
36457 Roo.tree.TreeSorter = function(tree, config){
36458     Roo.apply(this, config);
36459     tree.on("beforechildrenrendered", this.doSort, this);
36460     tree.on("append", this.updateSort, this);
36461     tree.on("insert", this.updateSort, this);
36462     
36463     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36464     var p = this.property || "text";
36465     var sortType = this.sortType;
36466     var fs = this.folderSort;
36467     var cs = this.caseSensitive === true;
36468     var leafAttr = this.leafAttr || 'leaf';
36469
36470     this.sortFn = function(n1, n2){
36471         if(fs){
36472             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36473                 return 1;
36474             }
36475             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36476                 return -1;
36477             }
36478         }
36479         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36480         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36481         if(v1 < v2){
36482                         return dsc ? +1 : -1;
36483                 }else if(v1 > v2){
36484                         return dsc ? -1 : +1;
36485         }else{
36486                 return 0;
36487         }
36488     };
36489 };
36490
36491 Roo.tree.TreeSorter.prototype = {
36492     doSort : function(node){
36493         node.sort(this.sortFn);
36494     },
36495     
36496     compareNodes : function(n1, n2){
36497         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36498     },
36499     
36500     updateSort : function(tree, node){
36501         if(node.childrenRendered){
36502             this.doSort.defer(1, this, [node]);
36503         }
36504     }
36505 };/*
36506  * Based on:
36507  * Ext JS Library 1.1.1
36508  * Copyright(c) 2006-2007, Ext JS, LLC.
36509  *
36510  * Originally Released Under LGPL - original licence link has changed is not relivant.
36511  *
36512  * Fork - LGPL
36513  * <script type="text/javascript">
36514  */
36515
36516 if(Roo.dd.DropZone){
36517     
36518 Roo.tree.TreeDropZone = function(tree, config){
36519     this.allowParentInsert = false;
36520     this.allowContainerDrop = false;
36521     this.appendOnly = false;
36522     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36523     this.tree = tree;
36524     this.lastInsertClass = "x-tree-no-status";
36525     this.dragOverData = {};
36526 };
36527
36528 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36529     ddGroup : "TreeDD",
36530     scroll:  true,
36531     
36532     expandDelay : 1000,
36533     
36534     expandNode : function(node){
36535         if(node.hasChildNodes() && !node.isExpanded()){
36536             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36537         }
36538     },
36539     
36540     queueExpand : function(node){
36541         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36542     },
36543     
36544     cancelExpand : function(){
36545         if(this.expandProcId){
36546             clearTimeout(this.expandProcId);
36547             this.expandProcId = false;
36548         }
36549     },
36550     
36551     isValidDropPoint : function(n, pt, dd, e, data){
36552         if(!n || !data){ return false; }
36553         var targetNode = n.node;
36554         var dropNode = data.node;
36555         // default drop rules
36556         if(!(targetNode && targetNode.isTarget && pt)){
36557             return false;
36558         }
36559         if(pt == "append" && targetNode.allowChildren === false){
36560             return false;
36561         }
36562         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36563             return false;
36564         }
36565         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36566             return false;
36567         }
36568         // reuse the object
36569         var overEvent = this.dragOverData;
36570         overEvent.tree = this.tree;
36571         overEvent.target = targetNode;
36572         overEvent.data = data;
36573         overEvent.point = pt;
36574         overEvent.source = dd;
36575         overEvent.rawEvent = e;
36576         overEvent.dropNode = dropNode;
36577         overEvent.cancel = false;  
36578         var result = this.tree.fireEvent("nodedragover", overEvent);
36579         return overEvent.cancel === false && result !== false;
36580     },
36581     
36582     getDropPoint : function(e, n, dd)
36583     {
36584         var tn = n.node;
36585         if(tn.isRoot){
36586             return tn.allowChildren !== false ? "append" : false; // always append for root
36587         }
36588         var dragEl = n.ddel;
36589         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36590         var y = Roo.lib.Event.getPageY(e);
36591         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36592         
36593         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36594         var noAppend = tn.allowChildren === false;
36595         if(this.appendOnly || tn.parentNode.allowChildren === false){
36596             return noAppend ? false : "append";
36597         }
36598         var noBelow = false;
36599         if(!this.allowParentInsert){
36600             noBelow = tn.hasChildNodes() && tn.isExpanded();
36601         }
36602         var q = (b - t) / (noAppend ? 2 : 3);
36603         if(y >= t && y < (t + q)){
36604             return "above";
36605         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36606             return "below";
36607         }else{
36608             return "append";
36609         }
36610     },
36611     
36612     onNodeEnter : function(n, dd, e, data)
36613     {
36614         this.cancelExpand();
36615     },
36616     
36617     onNodeOver : function(n, dd, e, data)
36618     {
36619        
36620         var pt = this.getDropPoint(e, n, dd);
36621         var node = n.node;
36622         
36623         // auto node expand check
36624         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36625             this.queueExpand(node);
36626         }else if(pt != "append"){
36627             this.cancelExpand();
36628         }
36629         
36630         // set the insert point style on the target node
36631         var returnCls = this.dropNotAllowed;
36632         if(this.isValidDropPoint(n, pt, dd, e, data)){
36633            if(pt){
36634                var el = n.ddel;
36635                var cls;
36636                if(pt == "above"){
36637                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36638                    cls = "x-tree-drag-insert-above";
36639                }else if(pt == "below"){
36640                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36641                    cls = "x-tree-drag-insert-below";
36642                }else{
36643                    returnCls = "x-tree-drop-ok-append";
36644                    cls = "x-tree-drag-append";
36645                }
36646                if(this.lastInsertClass != cls){
36647                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36648                    this.lastInsertClass = cls;
36649                }
36650            }
36651        }
36652        return returnCls;
36653     },
36654     
36655     onNodeOut : function(n, dd, e, data){
36656         
36657         this.cancelExpand();
36658         this.removeDropIndicators(n);
36659     },
36660     
36661     onNodeDrop : function(n, dd, e, data){
36662         var point = this.getDropPoint(e, n, dd);
36663         var targetNode = n.node;
36664         targetNode.ui.startDrop();
36665         if(!this.isValidDropPoint(n, point, dd, e, data)){
36666             targetNode.ui.endDrop();
36667             return false;
36668         }
36669         // first try to find the drop node
36670         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36671         var dropEvent = {
36672             tree : this.tree,
36673             target: targetNode,
36674             data: data,
36675             point: point,
36676             source: dd,
36677             rawEvent: e,
36678             dropNode: dropNode,
36679             cancel: !dropNode   
36680         };
36681         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36682         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36683             targetNode.ui.endDrop();
36684             return false;
36685         }
36686         // allow target changing
36687         targetNode = dropEvent.target;
36688         if(point == "append" && !targetNode.isExpanded()){
36689             targetNode.expand(false, null, function(){
36690                 this.completeDrop(dropEvent);
36691             }.createDelegate(this));
36692         }else{
36693             this.completeDrop(dropEvent);
36694         }
36695         return true;
36696     },
36697     
36698     completeDrop : function(de){
36699         var ns = de.dropNode, p = de.point, t = de.target;
36700         if(!(ns instanceof Array)){
36701             ns = [ns];
36702         }
36703         var n;
36704         for(var i = 0, len = ns.length; i < len; i++){
36705             n = ns[i];
36706             if(p == "above"){
36707                 t.parentNode.insertBefore(n, t);
36708             }else if(p == "below"){
36709                 t.parentNode.insertBefore(n, t.nextSibling);
36710             }else{
36711                 t.appendChild(n);
36712             }
36713         }
36714         n.ui.focus();
36715         if(this.tree.hlDrop){
36716             n.ui.highlight();
36717         }
36718         t.ui.endDrop();
36719         this.tree.fireEvent("nodedrop", de);
36720     },
36721     
36722     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36723         if(this.tree.hlDrop){
36724             dropNode.ui.focus();
36725             dropNode.ui.highlight();
36726         }
36727         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36728     },
36729     
36730     getTree : function(){
36731         return this.tree;
36732     },
36733     
36734     removeDropIndicators : function(n){
36735         if(n && n.ddel){
36736             var el = n.ddel;
36737             Roo.fly(el).removeClass([
36738                     "x-tree-drag-insert-above",
36739                     "x-tree-drag-insert-below",
36740                     "x-tree-drag-append"]);
36741             this.lastInsertClass = "_noclass";
36742         }
36743     },
36744     
36745     beforeDragDrop : function(target, e, id){
36746         this.cancelExpand();
36747         return true;
36748     },
36749     
36750     afterRepair : function(data){
36751         if(data && Roo.enableFx){
36752             data.node.ui.highlight();
36753         }
36754         this.hideProxy();
36755     } 
36756     
36757 });
36758
36759 }
36760 /*
36761  * Based on:
36762  * Ext JS Library 1.1.1
36763  * Copyright(c) 2006-2007, Ext JS, LLC.
36764  *
36765  * Originally Released Under LGPL - original licence link has changed is not relivant.
36766  *
36767  * Fork - LGPL
36768  * <script type="text/javascript">
36769  */
36770  
36771
36772 if(Roo.dd.DragZone){
36773 Roo.tree.TreeDragZone = function(tree, config){
36774     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36775     this.tree = tree;
36776 };
36777
36778 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36779     ddGroup : "TreeDD",
36780    
36781     onBeforeDrag : function(data, e){
36782         var n = data.node;
36783         return n && n.draggable && !n.disabled;
36784     },
36785      
36786     
36787     onInitDrag : function(e){
36788         var data = this.dragData;
36789         this.tree.getSelectionModel().select(data.node);
36790         this.proxy.update("");
36791         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36792         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36793     },
36794     
36795     getRepairXY : function(e, data){
36796         return data.node.ui.getDDRepairXY();
36797     },
36798     
36799     onEndDrag : function(data, e){
36800         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36801         
36802         
36803     },
36804     
36805     onValidDrop : function(dd, e, id){
36806         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36807         this.hideProxy();
36808     },
36809     
36810     beforeInvalidDrop : function(e, id){
36811         // this scrolls the original position back into view
36812         var sm = this.tree.getSelectionModel();
36813         sm.clearSelections();
36814         sm.select(this.dragData.node);
36815     }
36816 });
36817 }/*
36818  * Based on:
36819  * Ext JS Library 1.1.1
36820  * Copyright(c) 2006-2007, Ext JS, LLC.
36821  *
36822  * Originally Released Under LGPL - original licence link has changed is not relivant.
36823  *
36824  * Fork - LGPL
36825  * <script type="text/javascript">
36826  */
36827 /**
36828  * @class Roo.tree.TreeEditor
36829  * @extends Roo.Editor
36830  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36831  * as the editor field.
36832  * @constructor
36833  * @param {Object} config (used to be the tree panel.)
36834  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36835  * 
36836  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36837  * @cfg {Roo.form.TextField|Object} field The field configuration
36838  *
36839  * 
36840  */
36841 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36842     var tree = config;
36843     var field;
36844     if (oldconfig) { // old style..
36845         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36846     } else {
36847         // new style..
36848         tree = config.tree;
36849         config.field = config.field  || {};
36850         config.field.xtype = 'TextField';
36851         field = Roo.factory(config.field, Roo.form);
36852     }
36853     config = config || {};
36854     
36855     
36856     this.addEvents({
36857         /**
36858          * @event beforenodeedit
36859          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36860          * false from the handler of this event.
36861          * @param {Editor} this
36862          * @param {Roo.tree.Node} node 
36863          */
36864         "beforenodeedit" : true
36865     });
36866     
36867     //Roo.log(config);
36868     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36869
36870     this.tree = tree;
36871
36872     tree.on('beforeclick', this.beforeNodeClick, this);
36873     tree.getTreeEl().on('mousedown', this.hide, this);
36874     this.on('complete', this.updateNode, this);
36875     this.on('beforestartedit', this.fitToTree, this);
36876     this.on('startedit', this.bindScroll, this, {delay:10});
36877     this.on('specialkey', this.onSpecialKey, this);
36878 };
36879
36880 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36881     /**
36882      * @cfg {String} alignment
36883      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36884      */
36885     alignment: "l-l",
36886     // inherit
36887     autoSize: false,
36888     /**
36889      * @cfg {Boolean} hideEl
36890      * True to hide the bound element while the editor is displayed (defaults to false)
36891      */
36892     hideEl : false,
36893     /**
36894      * @cfg {String} cls
36895      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36896      */
36897     cls: "x-small-editor x-tree-editor",
36898     /**
36899      * @cfg {Boolean} shim
36900      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36901      */
36902     shim:false,
36903     // inherit
36904     shadow:"frame",
36905     /**
36906      * @cfg {Number} maxWidth
36907      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36908      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36909      * scroll and client offsets into account prior to each edit.
36910      */
36911     maxWidth: 250,
36912
36913     editDelay : 350,
36914
36915     // private
36916     fitToTree : function(ed, el){
36917         var td = this.tree.getTreeEl().dom, nd = el.dom;
36918         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36919             td.scrollLeft = nd.offsetLeft;
36920         }
36921         var w = Math.min(
36922                 this.maxWidth,
36923                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36924         this.setSize(w, '');
36925         
36926         return this.fireEvent('beforenodeedit', this, this.editNode);
36927         
36928     },
36929
36930     // private
36931     triggerEdit : function(node){
36932         this.completeEdit();
36933         this.editNode = node;
36934         this.startEdit(node.ui.textNode, node.text);
36935     },
36936
36937     // private
36938     bindScroll : function(){
36939         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36940     },
36941
36942     // private
36943     beforeNodeClick : function(node, e){
36944         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36945         this.lastClick = new Date();
36946         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36947             e.stopEvent();
36948             this.triggerEdit(node);
36949             return false;
36950         }
36951         return true;
36952     },
36953
36954     // private
36955     updateNode : function(ed, value){
36956         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36957         this.editNode.setText(value);
36958     },
36959
36960     // private
36961     onHide : function(){
36962         Roo.tree.TreeEditor.superclass.onHide.call(this);
36963         if(this.editNode){
36964             this.editNode.ui.focus();
36965         }
36966     },
36967
36968     // private
36969     onSpecialKey : function(field, e){
36970         var k = e.getKey();
36971         if(k == e.ESC){
36972             e.stopEvent();
36973             this.cancelEdit();
36974         }else if(k == e.ENTER && !e.hasModifier()){
36975             e.stopEvent();
36976             this.completeEdit();
36977         }
36978     }
36979 });//<Script type="text/javascript">
36980 /*
36981  * Based on:
36982  * Ext JS Library 1.1.1
36983  * Copyright(c) 2006-2007, Ext JS, LLC.
36984  *
36985  * Originally Released Under LGPL - original licence link has changed is not relivant.
36986  *
36987  * Fork - LGPL
36988  * <script type="text/javascript">
36989  */
36990  
36991 /**
36992  * Not documented??? - probably should be...
36993  */
36994
36995 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36996     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36997     
36998     renderElements : function(n, a, targetNode, bulkRender){
36999         //consel.log("renderElements?");
37000         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37001
37002         var t = n.getOwnerTree();
37003         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37004         
37005         var cols = t.columns;
37006         var bw = t.borderWidth;
37007         var c = cols[0];
37008         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37009          var cb = typeof a.checked == "boolean";
37010         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37011         var colcls = 'x-t-' + tid + '-c0';
37012         var buf = [
37013             '<li class="x-tree-node">',
37014             
37015                 
37016                 '<div class="x-tree-node-el ', a.cls,'">',
37017                     // extran...
37018                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37019                 
37020                 
37021                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37022                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37023                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37024                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37025                            (a.iconCls ? ' '+a.iconCls : ''),
37026                            '" unselectable="on" />',
37027                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37028                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37029                              
37030                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37031                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37032                             '<span unselectable="on" qtip="' + tx + '">',
37033                              tx,
37034                              '</span></a>' ,
37035                     '</div>',
37036                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37037                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37038                  ];
37039         for(var i = 1, len = cols.length; i < len; i++){
37040             c = cols[i];
37041             colcls = 'x-t-' + tid + '-c' +i;
37042             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37043             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37044                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37045                       "</div>");
37046          }
37047          
37048          buf.push(
37049             '</a>',
37050             '<div class="x-clear"></div></div>',
37051             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37052             "</li>");
37053         
37054         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37055             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37056                                 n.nextSibling.ui.getEl(), buf.join(""));
37057         }else{
37058             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37059         }
37060         var el = this.wrap.firstChild;
37061         this.elRow = el;
37062         this.elNode = el.firstChild;
37063         this.ranchor = el.childNodes[1];
37064         this.ctNode = this.wrap.childNodes[1];
37065         var cs = el.firstChild.childNodes;
37066         this.indentNode = cs[0];
37067         this.ecNode = cs[1];
37068         this.iconNode = cs[2];
37069         var index = 3;
37070         if(cb){
37071             this.checkbox = cs[3];
37072             index++;
37073         }
37074         this.anchor = cs[index];
37075         
37076         this.textNode = cs[index].firstChild;
37077         
37078         //el.on("click", this.onClick, this);
37079         //el.on("dblclick", this.onDblClick, this);
37080         
37081         
37082        // console.log(this);
37083     },
37084     initEvents : function(){
37085         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37086         
37087             
37088         var a = this.ranchor;
37089
37090         var el = Roo.get(a);
37091
37092         if(Roo.isOpera){ // opera render bug ignores the CSS
37093             el.setStyle("text-decoration", "none");
37094         }
37095
37096         el.on("click", this.onClick, this);
37097         el.on("dblclick", this.onDblClick, this);
37098         el.on("contextmenu", this.onContextMenu, this);
37099         
37100     },
37101     
37102     /*onSelectedChange : function(state){
37103         if(state){
37104             this.focus();
37105             this.addClass("x-tree-selected");
37106         }else{
37107             //this.blur();
37108             this.removeClass("x-tree-selected");
37109         }
37110     },*/
37111     addClass : function(cls){
37112         if(this.elRow){
37113             Roo.fly(this.elRow).addClass(cls);
37114         }
37115         
37116     },
37117     
37118     
37119     removeClass : function(cls){
37120         if(this.elRow){
37121             Roo.fly(this.elRow).removeClass(cls);
37122         }
37123     }
37124
37125     
37126     
37127 });//<Script type="text/javascript">
37128
37129 /*
37130  * Based on:
37131  * Ext JS Library 1.1.1
37132  * Copyright(c) 2006-2007, Ext JS, LLC.
37133  *
37134  * Originally Released Under LGPL - original licence link has changed is not relivant.
37135  *
37136  * Fork - LGPL
37137  * <script type="text/javascript">
37138  */
37139  
37140
37141 /**
37142  * @class Roo.tree.ColumnTree
37143  * @extends Roo.data.TreePanel
37144  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37145  * @cfg {int} borderWidth  compined right/left border allowance
37146  * @constructor
37147  * @param {String/HTMLElement/Element} el The container element
37148  * @param {Object} config
37149  */
37150 Roo.tree.ColumnTree =  function(el, config)
37151 {
37152    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37153    this.addEvents({
37154         /**
37155         * @event resize
37156         * Fire this event on a container when it resizes
37157         * @param {int} w Width
37158         * @param {int} h Height
37159         */
37160        "resize" : true
37161     });
37162     this.on('resize', this.onResize, this);
37163 };
37164
37165 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37166     //lines:false,
37167     
37168     
37169     borderWidth: Roo.isBorderBox ? 0 : 2, 
37170     headEls : false,
37171     
37172     render : function(){
37173         // add the header.....
37174        
37175         Roo.tree.ColumnTree.superclass.render.apply(this);
37176         
37177         this.el.addClass('x-column-tree');
37178         
37179         this.headers = this.el.createChild(
37180             {cls:'x-tree-headers'},this.innerCt.dom);
37181    
37182         var cols = this.columns, c;
37183         var totalWidth = 0;
37184         this.headEls = [];
37185         var  len = cols.length;
37186         for(var i = 0; i < len; i++){
37187              c = cols[i];
37188              totalWidth += c.width;
37189             this.headEls.push(this.headers.createChild({
37190                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37191                  cn: {
37192                      cls:'x-tree-hd-text',
37193                      html: c.header
37194                  },
37195                  style:'width:'+(c.width-this.borderWidth)+'px;'
37196              }));
37197         }
37198         this.headers.createChild({cls:'x-clear'});
37199         // prevent floats from wrapping when clipped
37200         this.headers.setWidth(totalWidth);
37201         //this.innerCt.setWidth(totalWidth);
37202         this.innerCt.setStyle({ overflow: 'auto' });
37203         this.onResize(this.width, this.height);
37204              
37205         
37206     },
37207     onResize : function(w,h)
37208     {
37209         this.height = h;
37210         this.width = w;
37211         // resize cols..
37212         this.innerCt.setWidth(this.width);
37213         this.innerCt.setHeight(this.height-20);
37214         
37215         // headers...
37216         var cols = this.columns, c;
37217         var totalWidth = 0;
37218         var expEl = false;
37219         var len = cols.length;
37220         for(var i = 0; i < len; i++){
37221             c = cols[i];
37222             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37223                 // it's the expander..
37224                 expEl  = this.headEls[i];
37225                 continue;
37226             }
37227             totalWidth += c.width;
37228             
37229         }
37230         if (expEl) {
37231             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37232         }
37233         this.headers.setWidth(w-20);
37234
37235         
37236         
37237         
37238     }
37239 });
37240 /*
37241  * Based on:
37242  * Ext JS Library 1.1.1
37243  * Copyright(c) 2006-2007, Ext JS, LLC.
37244  *
37245  * Originally Released Under LGPL - original licence link has changed is not relivant.
37246  *
37247  * Fork - LGPL
37248  * <script type="text/javascript">
37249  */
37250  
37251 /**
37252  * @class Roo.menu.Menu
37253  * @extends Roo.util.Observable
37254  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37255  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37256  * @constructor
37257  * Creates a new Menu
37258  * @param {Object} config Configuration options
37259  */
37260 Roo.menu.Menu = function(config){
37261     
37262     Roo.menu.Menu.superclass.constructor.call(this, config);
37263     
37264     this.id = this.id || Roo.id();
37265     this.addEvents({
37266         /**
37267          * @event beforeshow
37268          * Fires before this menu is displayed
37269          * @param {Roo.menu.Menu} this
37270          */
37271         beforeshow : true,
37272         /**
37273          * @event beforehide
37274          * Fires before this menu is hidden
37275          * @param {Roo.menu.Menu} this
37276          */
37277         beforehide : true,
37278         /**
37279          * @event show
37280          * Fires after this menu is displayed
37281          * @param {Roo.menu.Menu} this
37282          */
37283         show : true,
37284         /**
37285          * @event hide
37286          * Fires after this menu is hidden
37287          * @param {Roo.menu.Menu} this
37288          */
37289         hide : true,
37290         /**
37291          * @event click
37292          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37293          * @param {Roo.menu.Menu} this
37294          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37295          * @param {Roo.EventObject} e
37296          */
37297         click : true,
37298         /**
37299          * @event mouseover
37300          * Fires when the mouse is hovering over this menu
37301          * @param {Roo.menu.Menu} this
37302          * @param {Roo.EventObject} e
37303          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37304          */
37305         mouseover : true,
37306         /**
37307          * @event mouseout
37308          * Fires when the mouse exits this menu
37309          * @param {Roo.menu.Menu} this
37310          * @param {Roo.EventObject} e
37311          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37312          */
37313         mouseout : true,
37314         /**
37315          * @event itemclick
37316          * Fires when a menu item contained in this menu is clicked
37317          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37318          * @param {Roo.EventObject} e
37319          */
37320         itemclick: true
37321     });
37322     if (this.registerMenu) {
37323         Roo.menu.MenuMgr.register(this);
37324     }
37325     
37326     var mis = this.items;
37327     this.items = new Roo.util.MixedCollection();
37328     if(mis){
37329         this.add.apply(this, mis);
37330     }
37331 };
37332
37333 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37334     /**
37335      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37336      */
37337     minWidth : 120,
37338     /**
37339      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37340      * for bottom-right shadow (defaults to "sides")
37341      */
37342     shadow : "sides",
37343     /**
37344      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37345      * this menu (defaults to "tl-tr?")
37346      */
37347     subMenuAlign : "tl-tr?",
37348     /**
37349      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37350      * relative to its element of origin (defaults to "tl-bl?")
37351      */
37352     defaultAlign : "tl-bl?",
37353     /**
37354      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37355      */
37356     allowOtherMenus : false,
37357     /**
37358      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37359      */
37360     registerMenu : true,
37361
37362     hidden:true,
37363
37364     // private
37365     render : function(){
37366         if(this.el){
37367             return;
37368         }
37369         var el = this.el = new Roo.Layer({
37370             cls: "x-menu",
37371             shadow:this.shadow,
37372             constrain: false,
37373             parentEl: this.parentEl || document.body,
37374             zindex:15000
37375         });
37376
37377         this.keyNav = new Roo.menu.MenuNav(this);
37378
37379         if(this.plain){
37380             el.addClass("x-menu-plain");
37381         }
37382         if(this.cls){
37383             el.addClass(this.cls);
37384         }
37385         // generic focus element
37386         this.focusEl = el.createChild({
37387             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37388         });
37389         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37390         //disabling touch- as it's causing issues ..
37391         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37392         ul.on('click'   , this.onClick, this);
37393         
37394         
37395         ul.on("mouseover", this.onMouseOver, this);
37396         ul.on("mouseout", this.onMouseOut, this);
37397         this.items.each(function(item){
37398             if (item.hidden) {
37399                 return;
37400             }
37401             
37402             var li = document.createElement("li");
37403             li.className = "x-menu-list-item";
37404             ul.dom.appendChild(li);
37405             item.render(li, this);
37406         }, this);
37407         this.ul = ul;
37408         this.autoWidth();
37409     },
37410
37411     // private
37412     autoWidth : function(){
37413         var el = this.el, ul = this.ul;
37414         if(!el){
37415             return;
37416         }
37417         var w = this.width;
37418         if(w){
37419             el.setWidth(w);
37420         }else if(Roo.isIE){
37421             el.setWidth(this.minWidth);
37422             var t = el.dom.offsetWidth; // force recalc
37423             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37424         }
37425     },
37426
37427     // private
37428     delayAutoWidth : function(){
37429         if(this.rendered){
37430             if(!this.awTask){
37431                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37432             }
37433             this.awTask.delay(20);
37434         }
37435     },
37436
37437     // private
37438     findTargetItem : function(e){
37439         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37440         if(t && t.menuItemId){
37441             return this.items.get(t.menuItemId);
37442         }
37443     },
37444
37445     // private
37446     onClick : function(e){
37447         Roo.log("menu.onClick");
37448         var t = this.findTargetItem(e);
37449         if(!t){
37450             return;
37451         }
37452         Roo.log(e);
37453         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37454             if(t == this.activeItem && t.shouldDeactivate(e)){
37455                 this.activeItem.deactivate();
37456                 delete this.activeItem;
37457                 return;
37458             }
37459             if(t.canActivate){
37460                 this.setActiveItem(t, true);
37461             }
37462             return;
37463             
37464             
37465         }
37466         
37467         t.onClick(e);
37468         this.fireEvent("click", this, t, e);
37469     },
37470
37471     // private
37472     setActiveItem : function(item, autoExpand){
37473         if(item != this.activeItem){
37474             if(this.activeItem){
37475                 this.activeItem.deactivate();
37476             }
37477             this.activeItem = item;
37478             item.activate(autoExpand);
37479         }else if(autoExpand){
37480             item.expandMenu();
37481         }
37482     },
37483
37484     // private
37485     tryActivate : function(start, step){
37486         var items = this.items;
37487         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37488             var item = items.get(i);
37489             if(!item.disabled && item.canActivate){
37490                 this.setActiveItem(item, false);
37491                 return item;
37492             }
37493         }
37494         return false;
37495     },
37496
37497     // private
37498     onMouseOver : function(e){
37499         var t;
37500         if(t = this.findTargetItem(e)){
37501             if(t.canActivate && !t.disabled){
37502                 this.setActiveItem(t, true);
37503             }
37504         }
37505         this.fireEvent("mouseover", this, e, t);
37506     },
37507
37508     // private
37509     onMouseOut : function(e){
37510         var t;
37511         if(t = this.findTargetItem(e)){
37512             if(t == this.activeItem && t.shouldDeactivate(e)){
37513                 this.activeItem.deactivate();
37514                 delete this.activeItem;
37515             }
37516         }
37517         this.fireEvent("mouseout", this, e, t);
37518     },
37519
37520     /**
37521      * Read-only.  Returns true if the menu is currently displayed, else false.
37522      * @type Boolean
37523      */
37524     isVisible : function(){
37525         return this.el && !this.hidden;
37526     },
37527
37528     /**
37529      * Displays this menu relative to another element
37530      * @param {String/HTMLElement/Roo.Element} element The element to align to
37531      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37532      * the element (defaults to this.defaultAlign)
37533      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37534      */
37535     show : function(el, pos, parentMenu){
37536         this.parentMenu = parentMenu;
37537         if(!this.el){
37538             this.render();
37539         }
37540         this.fireEvent("beforeshow", this);
37541         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37542     },
37543
37544     /**
37545      * Displays this menu at a specific xy position
37546      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37547      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37548      */
37549     showAt : function(xy, parentMenu, /* private: */_e){
37550         this.parentMenu = parentMenu;
37551         if(!this.el){
37552             this.render();
37553         }
37554         if(_e !== false){
37555             this.fireEvent("beforeshow", this);
37556             xy = this.el.adjustForConstraints(xy);
37557         }
37558         this.el.setXY(xy);
37559         this.el.show();
37560         this.hidden = false;
37561         this.focus();
37562         this.fireEvent("show", this);
37563     },
37564
37565     focus : function(){
37566         if(!this.hidden){
37567             this.doFocus.defer(50, this);
37568         }
37569     },
37570
37571     doFocus : function(){
37572         if(!this.hidden){
37573             this.focusEl.focus();
37574         }
37575     },
37576
37577     /**
37578      * Hides this menu and optionally all parent menus
37579      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37580      */
37581     hide : function(deep){
37582         if(this.el && this.isVisible()){
37583             this.fireEvent("beforehide", this);
37584             if(this.activeItem){
37585                 this.activeItem.deactivate();
37586                 this.activeItem = null;
37587             }
37588             this.el.hide();
37589             this.hidden = true;
37590             this.fireEvent("hide", this);
37591         }
37592         if(deep === true && this.parentMenu){
37593             this.parentMenu.hide(true);
37594         }
37595     },
37596
37597     /**
37598      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37599      * Any of the following are valid:
37600      * <ul>
37601      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37602      * <li>An HTMLElement object which will be converted to a menu item</li>
37603      * <li>A menu item config object that will be created as a new menu item</li>
37604      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37605      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37606      * </ul>
37607      * Usage:
37608      * <pre><code>
37609 // Create the menu
37610 var menu = new Roo.menu.Menu();
37611
37612 // Create a menu item to add by reference
37613 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37614
37615 // Add a bunch of items at once using different methods.
37616 // Only the last item added will be returned.
37617 var item = menu.add(
37618     menuItem,                // add existing item by ref
37619     'Dynamic Item',          // new TextItem
37620     '-',                     // new separator
37621     { text: 'Config Item' }  // new item by config
37622 );
37623 </code></pre>
37624      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37625      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37626      */
37627     add : function(){
37628         var a = arguments, l = a.length, item;
37629         for(var i = 0; i < l; i++){
37630             var el = a[i];
37631             if ((typeof(el) == "object") && el.xtype && el.xns) {
37632                 el = Roo.factory(el, Roo.menu);
37633             }
37634             
37635             if(el.render){ // some kind of Item
37636                 item = this.addItem(el);
37637             }else if(typeof el == "string"){ // string
37638                 if(el == "separator" || el == "-"){
37639                     item = this.addSeparator();
37640                 }else{
37641                     item = this.addText(el);
37642                 }
37643             }else if(el.tagName || el.el){ // element
37644                 item = this.addElement(el);
37645             }else if(typeof el == "object"){ // must be menu item config?
37646                 item = this.addMenuItem(el);
37647             }
37648         }
37649         return item;
37650     },
37651
37652     /**
37653      * Returns this menu's underlying {@link Roo.Element} object
37654      * @return {Roo.Element} The element
37655      */
37656     getEl : function(){
37657         if(!this.el){
37658             this.render();
37659         }
37660         return this.el;
37661     },
37662
37663     /**
37664      * Adds a separator bar to the menu
37665      * @return {Roo.menu.Item} The menu item that was added
37666      */
37667     addSeparator : function(){
37668         return this.addItem(new Roo.menu.Separator());
37669     },
37670
37671     /**
37672      * Adds an {@link Roo.Element} object to the menu
37673      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37674      * @return {Roo.menu.Item} The menu item that was added
37675      */
37676     addElement : function(el){
37677         return this.addItem(new Roo.menu.BaseItem(el));
37678     },
37679
37680     /**
37681      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37682      * @param {Roo.menu.Item} item The menu item to add
37683      * @return {Roo.menu.Item} The menu item that was added
37684      */
37685     addItem : function(item){
37686         this.items.add(item);
37687         if(this.ul){
37688             var li = document.createElement("li");
37689             li.className = "x-menu-list-item";
37690             this.ul.dom.appendChild(li);
37691             item.render(li, this);
37692             this.delayAutoWidth();
37693         }
37694         return item;
37695     },
37696
37697     /**
37698      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37699      * @param {Object} config A MenuItem config object
37700      * @return {Roo.menu.Item} The menu item that was added
37701      */
37702     addMenuItem : function(config){
37703         if(!(config instanceof Roo.menu.Item)){
37704             if(typeof config.checked == "boolean"){ // must be check menu item config?
37705                 config = new Roo.menu.CheckItem(config);
37706             }else{
37707                 config = new Roo.menu.Item(config);
37708             }
37709         }
37710         return this.addItem(config);
37711     },
37712
37713     /**
37714      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37715      * @param {String} text The text to display in the menu item
37716      * @return {Roo.menu.Item} The menu item that was added
37717      */
37718     addText : function(text){
37719         return this.addItem(new Roo.menu.TextItem({ text : text }));
37720     },
37721
37722     /**
37723      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37724      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37725      * @param {Roo.menu.Item} item The menu item to add
37726      * @return {Roo.menu.Item} The menu item that was added
37727      */
37728     insert : function(index, item){
37729         this.items.insert(index, item);
37730         if(this.ul){
37731             var li = document.createElement("li");
37732             li.className = "x-menu-list-item";
37733             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37734             item.render(li, this);
37735             this.delayAutoWidth();
37736         }
37737         return item;
37738     },
37739
37740     /**
37741      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37742      * @param {Roo.menu.Item} item The menu item to remove
37743      */
37744     remove : function(item){
37745         this.items.removeKey(item.id);
37746         item.destroy();
37747     },
37748
37749     /**
37750      * Removes and destroys all items in the menu
37751      */
37752     removeAll : function(){
37753         var f;
37754         while(f = this.items.first()){
37755             this.remove(f);
37756         }
37757     }
37758 });
37759
37760 // MenuNav is a private utility class used internally by the Menu
37761 Roo.menu.MenuNav = function(menu){
37762     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37763     this.scope = this.menu = menu;
37764 };
37765
37766 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37767     doRelay : function(e, h){
37768         var k = e.getKey();
37769         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37770             this.menu.tryActivate(0, 1);
37771             return false;
37772         }
37773         return h.call(this.scope || this, e, this.menu);
37774     },
37775
37776     up : function(e, m){
37777         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37778             m.tryActivate(m.items.length-1, -1);
37779         }
37780     },
37781
37782     down : function(e, m){
37783         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37784             m.tryActivate(0, 1);
37785         }
37786     },
37787
37788     right : function(e, m){
37789         if(m.activeItem){
37790             m.activeItem.expandMenu(true);
37791         }
37792     },
37793
37794     left : function(e, m){
37795         m.hide();
37796         if(m.parentMenu && m.parentMenu.activeItem){
37797             m.parentMenu.activeItem.activate();
37798         }
37799     },
37800
37801     enter : function(e, m){
37802         if(m.activeItem){
37803             e.stopPropagation();
37804             m.activeItem.onClick(e);
37805             m.fireEvent("click", this, m.activeItem);
37806             return true;
37807         }
37808     }
37809 });/*
37810  * Based on:
37811  * Ext JS Library 1.1.1
37812  * Copyright(c) 2006-2007, Ext JS, LLC.
37813  *
37814  * Originally Released Under LGPL - original licence link has changed is not relivant.
37815  *
37816  * Fork - LGPL
37817  * <script type="text/javascript">
37818  */
37819  
37820 /**
37821  * @class Roo.menu.MenuMgr
37822  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37823  * @singleton
37824  */
37825 Roo.menu.MenuMgr = function(){
37826    var menus, active, groups = {}, attached = false, lastShow = new Date();
37827
37828    // private - called when first menu is created
37829    function init(){
37830        menus = {};
37831        active = new Roo.util.MixedCollection();
37832        Roo.get(document).addKeyListener(27, function(){
37833            if(active.length > 0){
37834                hideAll();
37835            }
37836        });
37837    }
37838
37839    // private
37840    function hideAll(){
37841        if(active && active.length > 0){
37842            var c = active.clone();
37843            c.each(function(m){
37844                m.hide();
37845            });
37846        }
37847    }
37848
37849    // private
37850    function onHide(m){
37851        active.remove(m);
37852        if(active.length < 1){
37853            Roo.get(document).un("mousedown", onMouseDown);
37854            attached = false;
37855        }
37856    }
37857
37858    // private
37859    function onShow(m){
37860        var last = active.last();
37861        lastShow = new Date();
37862        active.add(m);
37863        if(!attached){
37864            Roo.get(document).on("mousedown", onMouseDown);
37865            attached = true;
37866        }
37867        if(m.parentMenu){
37868           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37869           m.parentMenu.activeChild = m;
37870        }else if(last && last.isVisible()){
37871           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37872        }
37873    }
37874
37875    // private
37876    function onBeforeHide(m){
37877        if(m.activeChild){
37878            m.activeChild.hide();
37879        }
37880        if(m.autoHideTimer){
37881            clearTimeout(m.autoHideTimer);
37882            delete m.autoHideTimer;
37883        }
37884    }
37885
37886    // private
37887    function onBeforeShow(m){
37888        var pm = m.parentMenu;
37889        if(!pm && !m.allowOtherMenus){
37890            hideAll();
37891        }else if(pm && pm.activeChild && active != m){
37892            pm.activeChild.hide();
37893        }
37894    }
37895
37896    // private
37897    function onMouseDown(e){
37898        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37899            hideAll();
37900        }
37901    }
37902
37903    // private
37904    function onBeforeCheck(mi, state){
37905        if(state){
37906            var g = groups[mi.group];
37907            for(var i = 0, l = g.length; i < l; i++){
37908                if(g[i] != mi){
37909                    g[i].setChecked(false);
37910                }
37911            }
37912        }
37913    }
37914
37915    return {
37916
37917        /**
37918         * Hides all menus that are currently visible
37919         */
37920        hideAll : function(){
37921             hideAll();  
37922        },
37923
37924        // private
37925        register : function(menu){
37926            if(!menus){
37927                init();
37928            }
37929            menus[menu.id] = menu;
37930            menu.on("beforehide", onBeforeHide);
37931            menu.on("hide", onHide);
37932            menu.on("beforeshow", onBeforeShow);
37933            menu.on("show", onShow);
37934            var g = menu.group;
37935            if(g && menu.events["checkchange"]){
37936                if(!groups[g]){
37937                    groups[g] = [];
37938                }
37939                groups[g].push(menu);
37940                menu.on("checkchange", onCheck);
37941            }
37942        },
37943
37944         /**
37945          * Returns a {@link Roo.menu.Menu} object
37946          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37947          * be used to generate and return a new Menu instance.
37948          */
37949        get : function(menu){
37950            if(typeof menu == "string"){ // menu id
37951                return menus[menu];
37952            }else if(menu.events){  // menu instance
37953                return menu;
37954            }else if(typeof menu.length == 'number'){ // array of menu items?
37955                return new Roo.menu.Menu({items:menu});
37956            }else{ // otherwise, must be a config
37957                return new Roo.menu.Menu(menu);
37958            }
37959        },
37960
37961        // private
37962        unregister : function(menu){
37963            delete menus[menu.id];
37964            menu.un("beforehide", onBeforeHide);
37965            menu.un("hide", onHide);
37966            menu.un("beforeshow", onBeforeShow);
37967            menu.un("show", onShow);
37968            var g = menu.group;
37969            if(g && menu.events["checkchange"]){
37970                groups[g].remove(menu);
37971                menu.un("checkchange", onCheck);
37972            }
37973        },
37974
37975        // private
37976        registerCheckable : function(menuItem){
37977            var g = menuItem.group;
37978            if(g){
37979                if(!groups[g]){
37980                    groups[g] = [];
37981                }
37982                groups[g].push(menuItem);
37983                menuItem.on("beforecheckchange", onBeforeCheck);
37984            }
37985        },
37986
37987        // private
37988        unregisterCheckable : function(menuItem){
37989            var g = menuItem.group;
37990            if(g){
37991                groups[g].remove(menuItem);
37992                menuItem.un("beforecheckchange", onBeforeCheck);
37993            }
37994        }
37995    };
37996 }();/*
37997  * Based on:
37998  * Ext JS Library 1.1.1
37999  * Copyright(c) 2006-2007, Ext JS, LLC.
38000  *
38001  * Originally Released Under LGPL - original licence link has changed is not relivant.
38002  *
38003  * Fork - LGPL
38004  * <script type="text/javascript">
38005  */
38006  
38007
38008 /**
38009  * @class Roo.menu.BaseItem
38010  * @extends Roo.Component
38011  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38012  * management and base configuration options shared by all menu components.
38013  * @constructor
38014  * Creates a new BaseItem
38015  * @param {Object} config Configuration options
38016  */
38017 Roo.menu.BaseItem = function(config){
38018     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38019
38020     this.addEvents({
38021         /**
38022          * @event click
38023          * Fires when this item is clicked
38024          * @param {Roo.menu.BaseItem} this
38025          * @param {Roo.EventObject} e
38026          */
38027         click: true,
38028         /**
38029          * @event activate
38030          * Fires when this item is activated
38031          * @param {Roo.menu.BaseItem} this
38032          */
38033         activate : true,
38034         /**
38035          * @event deactivate
38036          * Fires when this item is deactivated
38037          * @param {Roo.menu.BaseItem} this
38038          */
38039         deactivate : true
38040     });
38041
38042     if(this.handler){
38043         this.on("click", this.handler, this.scope, true);
38044     }
38045 };
38046
38047 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38048     /**
38049      * @cfg {Function} handler
38050      * A function that will handle the click event of this menu item (defaults to undefined)
38051      */
38052     /**
38053      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38054      */
38055     canActivate : false,
38056     
38057      /**
38058      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38059      */
38060     hidden: false,
38061     
38062     /**
38063      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38064      */
38065     activeClass : "x-menu-item-active",
38066     /**
38067      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38068      */
38069     hideOnClick : true,
38070     /**
38071      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38072      */
38073     hideDelay : 100,
38074
38075     // private
38076     ctype: "Roo.menu.BaseItem",
38077
38078     // private
38079     actionMode : "container",
38080
38081     // private
38082     render : function(container, parentMenu){
38083         this.parentMenu = parentMenu;
38084         Roo.menu.BaseItem.superclass.render.call(this, container);
38085         this.container.menuItemId = this.id;
38086     },
38087
38088     // private
38089     onRender : function(container, position){
38090         this.el = Roo.get(this.el);
38091         container.dom.appendChild(this.el.dom);
38092     },
38093
38094     // private
38095     onClick : function(e){
38096         if(!this.disabled && this.fireEvent("click", this, e) !== false
38097                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38098             this.handleClick(e);
38099         }else{
38100             e.stopEvent();
38101         }
38102     },
38103
38104     // private
38105     activate : function(){
38106         if(this.disabled){
38107             return false;
38108         }
38109         var li = this.container;
38110         li.addClass(this.activeClass);
38111         this.region = li.getRegion().adjust(2, 2, -2, -2);
38112         this.fireEvent("activate", this);
38113         return true;
38114     },
38115
38116     // private
38117     deactivate : function(){
38118         this.container.removeClass(this.activeClass);
38119         this.fireEvent("deactivate", this);
38120     },
38121
38122     // private
38123     shouldDeactivate : function(e){
38124         return !this.region || !this.region.contains(e.getPoint());
38125     },
38126
38127     // private
38128     handleClick : function(e){
38129         if(this.hideOnClick){
38130             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38131         }
38132     },
38133
38134     // private
38135     expandMenu : function(autoActivate){
38136         // do nothing
38137     },
38138
38139     // private
38140     hideMenu : function(){
38141         // do nothing
38142     }
38143 });/*
38144  * Based on:
38145  * Ext JS Library 1.1.1
38146  * Copyright(c) 2006-2007, Ext JS, LLC.
38147  *
38148  * Originally Released Under LGPL - original licence link has changed is not relivant.
38149  *
38150  * Fork - LGPL
38151  * <script type="text/javascript">
38152  */
38153  
38154 /**
38155  * @class Roo.menu.Adapter
38156  * @extends Roo.menu.BaseItem
38157  * 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.
38158  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38159  * @constructor
38160  * Creates a new Adapter
38161  * @param {Object} config Configuration options
38162  */
38163 Roo.menu.Adapter = function(component, config){
38164     Roo.menu.Adapter.superclass.constructor.call(this, config);
38165     this.component = component;
38166 };
38167 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38168     // private
38169     canActivate : true,
38170
38171     // private
38172     onRender : function(container, position){
38173         this.component.render(container);
38174         this.el = this.component.getEl();
38175     },
38176
38177     // private
38178     activate : function(){
38179         if(this.disabled){
38180             return false;
38181         }
38182         this.component.focus();
38183         this.fireEvent("activate", this);
38184         return true;
38185     },
38186
38187     // private
38188     deactivate : function(){
38189         this.fireEvent("deactivate", this);
38190     },
38191
38192     // private
38193     disable : function(){
38194         this.component.disable();
38195         Roo.menu.Adapter.superclass.disable.call(this);
38196     },
38197
38198     // private
38199     enable : function(){
38200         this.component.enable();
38201         Roo.menu.Adapter.superclass.enable.call(this);
38202     }
38203 });/*
38204  * Based on:
38205  * Ext JS Library 1.1.1
38206  * Copyright(c) 2006-2007, Ext JS, LLC.
38207  *
38208  * Originally Released Under LGPL - original licence link has changed is not relivant.
38209  *
38210  * Fork - LGPL
38211  * <script type="text/javascript">
38212  */
38213
38214 /**
38215  * @class Roo.menu.TextItem
38216  * @extends Roo.menu.BaseItem
38217  * Adds a static text string to a menu, usually used as either a heading or group separator.
38218  * Note: old style constructor with text is still supported.
38219  * 
38220  * @constructor
38221  * Creates a new TextItem
38222  * @param {Object} cfg Configuration
38223  */
38224 Roo.menu.TextItem = function(cfg){
38225     if (typeof(cfg) == 'string') {
38226         this.text = cfg;
38227     } else {
38228         Roo.apply(this,cfg);
38229     }
38230     
38231     Roo.menu.TextItem.superclass.constructor.call(this);
38232 };
38233
38234 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38235     /**
38236      * @cfg {Boolean} text Text to show on item.
38237      */
38238     text : '',
38239     
38240     /**
38241      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38242      */
38243     hideOnClick : false,
38244     /**
38245      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38246      */
38247     itemCls : "x-menu-text",
38248
38249     // private
38250     onRender : function(){
38251         var s = document.createElement("span");
38252         s.className = this.itemCls;
38253         s.innerHTML = this.text;
38254         this.el = s;
38255         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38256     }
38257 });/*
38258  * Based on:
38259  * Ext JS Library 1.1.1
38260  * Copyright(c) 2006-2007, Ext JS, LLC.
38261  *
38262  * Originally Released Under LGPL - original licence link has changed is not relivant.
38263  *
38264  * Fork - LGPL
38265  * <script type="text/javascript">
38266  */
38267
38268 /**
38269  * @class Roo.menu.Separator
38270  * @extends Roo.menu.BaseItem
38271  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38272  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38273  * @constructor
38274  * @param {Object} config Configuration options
38275  */
38276 Roo.menu.Separator = function(config){
38277     Roo.menu.Separator.superclass.constructor.call(this, config);
38278 };
38279
38280 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38281     /**
38282      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38283      */
38284     itemCls : "x-menu-sep",
38285     /**
38286      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38287      */
38288     hideOnClick : false,
38289
38290     // private
38291     onRender : function(li){
38292         var s = document.createElement("span");
38293         s.className = this.itemCls;
38294         s.innerHTML = "&#160;";
38295         this.el = s;
38296         li.addClass("x-menu-sep-li");
38297         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38298     }
38299 });/*
38300  * Based on:
38301  * Ext JS Library 1.1.1
38302  * Copyright(c) 2006-2007, Ext JS, LLC.
38303  *
38304  * Originally Released Under LGPL - original licence link has changed is not relivant.
38305  *
38306  * Fork - LGPL
38307  * <script type="text/javascript">
38308  */
38309 /**
38310  * @class Roo.menu.Item
38311  * @extends Roo.menu.BaseItem
38312  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38313  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38314  * activation and click handling.
38315  * @constructor
38316  * Creates a new Item
38317  * @param {Object} config Configuration options
38318  */
38319 Roo.menu.Item = function(config){
38320     Roo.menu.Item.superclass.constructor.call(this, config);
38321     if(this.menu){
38322         this.menu = Roo.menu.MenuMgr.get(this.menu);
38323     }
38324 };
38325 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38326     
38327     /**
38328      * @cfg {String} text
38329      * The text to show on the menu item.
38330      */
38331     text: '',
38332      /**
38333      * @cfg {String} HTML to render in menu
38334      * The text to show on the menu item (HTML version).
38335      */
38336     html: '',
38337     /**
38338      * @cfg {String} icon
38339      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38340      */
38341     icon: undefined,
38342     /**
38343      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38344      */
38345     itemCls : "x-menu-item",
38346     /**
38347      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38348      */
38349     canActivate : true,
38350     /**
38351      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38352      */
38353     showDelay: 200,
38354     // doc'd in BaseItem
38355     hideDelay: 200,
38356
38357     // private
38358     ctype: "Roo.menu.Item",
38359     
38360     // private
38361     onRender : function(container, position){
38362         var el = document.createElement("a");
38363         el.hideFocus = true;
38364         el.unselectable = "on";
38365         el.href = this.href || "#";
38366         if(this.hrefTarget){
38367             el.target = this.hrefTarget;
38368         }
38369         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38370         
38371         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38372         
38373         el.innerHTML = String.format(
38374                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38375                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38376         this.el = el;
38377         Roo.menu.Item.superclass.onRender.call(this, container, position);
38378     },
38379
38380     /**
38381      * Sets the text to display in this menu item
38382      * @param {String} text The text to display
38383      * @param {Boolean} isHTML true to indicate text is pure html.
38384      */
38385     setText : function(text, isHTML){
38386         if (isHTML) {
38387             this.html = text;
38388         } else {
38389             this.text = text;
38390             this.html = '';
38391         }
38392         if(this.rendered){
38393             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38394      
38395             this.el.update(String.format(
38396                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38397                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38398             this.parentMenu.autoWidth();
38399         }
38400     },
38401
38402     // private
38403     handleClick : function(e){
38404         if(!this.href){ // if no link defined, stop the event automatically
38405             e.stopEvent();
38406         }
38407         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38408     },
38409
38410     // private
38411     activate : function(autoExpand){
38412         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38413             this.focus();
38414             if(autoExpand){
38415                 this.expandMenu();
38416             }
38417         }
38418         return true;
38419     },
38420
38421     // private
38422     shouldDeactivate : function(e){
38423         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38424             if(this.menu && this.menu.isVisible()){
38425                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38426             }
38427             return true;
38428         }
38429         return false;
38430     },
38431
38432     // private
38433     deactivate : function(){
38434         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38435         this.hideMenu();
38436     },
38437
38438     // private
38439     expandMenu : function(autoActivate){
38440         if(!this.disabled && this.menu){
38441             clearTimeout(this.hideTimer);
38442             delete this.hideTimer;
38443             if(!this.menu.isVisible() && !this.showTimer){
38444                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38445             }else if (this.menu.isVisible() && autoActivate){
38446                 this.menu.tryActivate(0, 1);
38447             }
38448         }
38449     },
38450
38451     // private
38452     deferExpand : function(autoActivate){
38453         delete this.showTimer;
38454         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38455         if(autoActivate){
38456             this.menu.tryActivate(0, 1);
38457         }
38458     },
38459
38460     // private
38461     hideMenu : function(){
38462         clearTimeout(this.showTimer);
38463         delete this.showTimer;
38464         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38465             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38466         }
38467     },
38468
38469     // private
38470     deferHide : function(){
38471         delete this.hideTimer;
38472         this.menu.hide();
38473     }
38474 });/*
38475  * Based on:
38476  * Ext JS Library 1.1.1
38477  * Copyright(c) 2006-2007, Ext JS, LLC.
38478  *
38479  * Originally Released Under LGPL - original licence link has changed is not relivant.
38480  *
38481  * Fork - LGPL
38482  * <script type="text/javascript">
38483  */
38484  
38485 /**
38486  * @class Roo.menu.CheckItem
38487  * @extends Roo.menu.Item
38488  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38489  * @constructor
38490  * Creates a new CheckItem
38491  * @param {Object} config Configuration options
38492  */
38493 Roo.menu.CheckItem = function(config){
38494     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38495     this.addEvents({
38496         /**
38497          * @event beforecheckchange
38498          * Fires before the checked value is set, providing an opportunity to cancel if needed
38499          * @param {Roo.menu.CheckItem} this
38500          * @param {Boolean} checked The new checked value that will be set
38501          */
38502         "beforecheckchange" : true,
38503         /**
38504          * @event checkchange
38505          * Fires after the checked value has been set
38506          * @param {Roo.menu.CheckItem} this
38507          * @param {Boolean} checked The checked value that was set
38508          */
38509         "checkchange" : true
38510     });
38511     if(this.checkHandler){
38512         this.on('checkchange', this.checkHandler, this.scope);
38513     }
38514 };
38515 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38516     /**
38517      * @cfg {String} group
38518      * All check items with the same group name will automatically be grouped into a single-select
38519      * radio button group (defaults to '')
38520      */
38521     /**
38522      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38523      */
38524     itemCls : "x-menu-item x-menu-check-item",
38525     /**
38526      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38527      */
38528     groupClass : "x-menu-group-item",
38529
38530     /**
38531      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38532      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38533      * initialized with checked = true will be rendered as checked.
38534      */
38535     checked: false,
38536
38537     // private
38538     ctype: "Roo.menu.CheckItem",
38539
38540     // private
38541     onRender : function(c){
38542         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38543         if(this.group){
38544             this.el.addClass(this.groupClass);
38545         }
38546         Roo.menu.MenuMgr.registerCheckable(this);
38547         if(this.checked){
38548             this.checked = false;
38549             this.setChecked(true, true);
38550         }
38551     },
38552
38553     // private
38554     destroy : function(){
38555         if(this.rendered){
38556             Roo.menu.MenuMgr.unregisterCheckable(this);
38557         }
38558         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38559     },
38560
38561     /**
38562      * Set the checked state of this item
38563      * @param {Boolean} checked The new checked value
38564      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38565      */
38566     setChecked : function(state, suppressEvent){
38567         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38568             if(this.container){
38569                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38570             }
38571             this.checked = state;
38572             if(suppressEvent !== true){
38573                 this.fireEvent("checkchange", this, state);
38574             }
38575         }
38576     },
38577
38578     // private
38579     handleClick : function(e){
38580        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38581            this.setChecked(!this.checked);
38582        }
38583        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38584     }
38585 });/*
38586  * Based on:
38587  * Ext JS Library 1.1.1
38588  * Copyright(c) 2006-2007, Ext JS, LLC.
38589  *
38590  * Originally Released Under LGPL - original licence link has changed is not relivant.
38591  *
38592  * Fork - LGPL
38593  * <script type="text/javascript">
38594  */
38595  
38596 /**
38597  * @class Roo.menu.DateItem
38598  * @extends Roo.menu.Adapter
38599  * A menu item that wraps the {@link Roo.DatPicker} component.
38600  * @constructor
38601  * Creates a new DateItem
38602  * @param {Object} config Configuration options
38603  */
38604 Roo.menu.DateItem = function(config){
38605     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38606     /** The Roo.DatePicker object @type Roo.DatePicker */
38607     this.picker = this.component;
38608     this.addEvents({select: true});
38609     
38610     this.picker.on("render", function(picker){
38611         picker.getEl().swallowEvent("click");
38612         picker.container.addClass("x-menu-date-item");
38613     });
38614
38615     this.picker.on("select", this.onSelect, this);
38616 };
38617
38618 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38619     // private
38620     onSelect : function(picker, date){
38621         this.fireEvent("select", this, date, picker);
38622         Roo.menu.DateItem.superclass.handleClick.call(this);
38623     }
38624 });/*
38625  * Based on:
38626  * Ext JS Library 1.1.1
38627  * Copyright(c) 2006-2007, Ext JS, LLC.
38628  *
38629  * Originally Released Under LGPL - original licence link has changed is not relivant.
38630  *
38631  * Fork - LGPL
38632  * <script type="text/javascript">
38633  */
38634  
38635 /**
38636  * @class Roo.menu.ColorItem
38637  * @extends Roo.menu.Adapter
38638  * A menu item that wraps the {@link Roo.ColorPalette} component.
38639  * @constructor
38640  * Creates a new ColorItem
38641  * @param {Object} config Configuration options
38642  */
38643 Roo.menu.ColorItem = function(config){
38644     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38645     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38646     this.palette = this.component;
38647     this.relayEvents(this.palette, ["select"]);
38648     if(this.selectHandler){
38649         this.on('select', this.selectHandler, this.scope);
38650     }
38651 };
38652 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38653  * Based on:
38654  * Ext JS Library 1.1.1
38655  * Copyright(c) 2006-2007, Ext JS, LLC.
38656  *
38657  * Originally Released Under LGPL - original licence link has changed is not relivant.
38658  *
38659  * Fork - LGPL
38660  * <script type="text/javascript">
38661  */
38662  
38663
38664 /**
38665  * @class Roo.menu.DateMenu
38666  * @extends Roo.menu.Menu
38667  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38668  * @constructor
38669  * Creates a new DateMenu
38670  * @param {Object} config Configuration options
38671  */
38672 Roo.menu.DateMenu = function(config){
38673     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38674     this.plain = true;
38675     var di = new Roo.menu.DateItem(config);
38676     this.add(di);
38677     /**
38678      * The {@link Roo.DatePicker} instance for this DateMenu
38679      * @type DatePicker
38680      */
38681     this.picker = di.picker;
38682     /**
38683      * @event select
38684      * @param {DatePicker} picker
38685      * @param {Date} date
38686      */
38687     this.relayEvents(di, ["select"]);
38688     this.on('beforeshow', function(){
38689         if(this.picker){
38690             this.picker.hideMonthPicker(false);
38691         }
38692     }, this);
38693 };
38694 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38695     cls:'x-date-menu'
38696 });/*
38697  * Based on:
38698  * Ext JS Library 1.1.1
38699  * Copyright(c) 2006-2007, Ext JS, LLC.
38700  *
38701  * Originally Released Under LGPL - original licence link has changed is not relivant.
38702  *
38703  * Fork - LGPL
38704  * <script type="text/javascript">
38705  */
38706  
38707
38708 /**
38709  * @class Roo.menu.ColorMenu
38710  * @extends Roo.menu.Menu
38711  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38712  * @constructor
38713  * Creates a new ColorMenu
38714  * @param {Object} config Configuration options
38715  */
38716 Roo.menu.ColorMenu = function(config){
38717     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38718     this.plain = true;
38719     var ci = new Roo.menu.ColorItem(config);
38720     this.add(ci);
38721     /**
38722      * The {@link Roo.ColorPalette} instance for this ColorMenu
38723      * @type ColorPalette
38724      */
38725     this.palette = ci.palette;
38726     /**
38727      * @event select
38728      * @param {ColorPalette} palette
38729      * @param {String} color
38730      */
38731     this.relayEvents(ci, ["select"]);
38732 };
38733 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38734  * Based on:
38735  * Ext JS Library 1.1.1
38736  * Copyright(c) 2006-2007, Ext JS, LLC.
38737  *
38738  * Originally Released Under LGPL - original licence link has changed is not relivant.
38739  *
38740  * Fork - LGPL
38741  * <script type="text/javascript">
38742  */
38743  
38744 /**
38745  * @class Roo.form.TextItem
38746  * @extends Roo.BoxComponent
38747  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38748  * @constructor
38749  * Creates a new TextItem
38750  * @param {Object} config Configuration options
38751  */
38752 Roo.form.TextItem = function(config){
38753     Roo.form.TextItem.superclass.constructor.call(this, config);
38754 };
38755
38756 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
38757     
38758     /**
38759      * @cfg {String} tag the tag for this item (default div)
38760      */
38761     tag : 'div',
38762     /**
38763      * @cfg {String} html the content for this item
38764      */
38765     html : '',
38766     
38767     getAutoCreate : function()
38768     {
38769         var cfg = {
38770             id: this.id,
38771             tag: this.tag,
38772             html: this.html,
38773             cls: 'x-form-item'
38774         };
38775         
38776         return cfg;
38777         
38778     },
38779     
38780     onRender : function(ct, position)
38781     {
38782         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
38783         
38784         if(!this.el){
38785             var cfg = this.getAutoCreate();
38786             if(!cfg.name){
38787                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38788             }
38789             if (!cfg.name.length) {
38790                 delete cfg.name;
38791             }
38792             this.el = ct.createChild(cfg, position);
38793         }
38794     }
38795     
38796 });/*
38797  * Based on:
38798  * Ext JS Library 1.1.1
38799  * Copyright(c) 2006-2007, Ext JS, LLC.
38800  *
38801  * Originally Released Under LGPL - original licence link has changed is not relivant.
38802  *
38803  * Fork - LGPL
38804  * <script type="text/javascript">
38805  */
38806  
38807 /**
38808  * @class Roo.form.Field
38809  * @extends Roo.BoxComponent
38810  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38811  * @constructor
38812  * Creates a new Field
38813  * @param {Object} config Configuration options
38814  */
38815 Roo.form.Field = function(config){
38816     Roo.form.Field.superclass.constructor.call(this, config);
38817 };
38818
38819 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38820     /**
38821      * @cfg {String} fieldLabel Label to use when rendering a form.
38822      */
38823        /**
38824      * @cfg {String} qtip Mouse over tip
38825      */
38826      
38827     /**
38828      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38829      */
38830     invalidClass : "x-form-invalid",
38831     /**
38832      * @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")
38833      */
38834     invalidText : "The value in this field is invalid",
38835     /**
38836      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38837      */
38838     focusClass : "x-form-focus",
38839     /**
38840      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38841       automatic validation (defaults to "keyup").
38842      */
38843     validationEvent : "keyup",
38844     /**
38845      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38846      */
38847     validateOnBlur : true,
38848     /**
38849      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38850      */
38851     validationDelay : 250,
38852     /**
38853      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38854      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38855      */
38856     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38857     /**
38858      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38859      */
38860     fieldClass : "x-form-field",
38861     /**
38862      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38863      *<pre>
38864 Value         Description
38865 -----------   ----------------------------------------------------------------------
38866 qtip          Display a quick tip when the user hovers over the field
38867 title         Display a default browser title attribute popup
38868 under         Add a block div beneath the field containing the error text
38869 side          Add an error icon to the right of the field with a popup on hover
38870 [element id]  Add the error text directly to the innerHTML of the specified element
38871 </pre>
38872      */
38873     msgTarget : 'qtip',
38874     /**
38875      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38876      */
38877     msgFx : 'normal',
38878
38879     /**
38880      * @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.
38881      */
38882     readOnly : false,
38883
38884     /**
38885      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38886      */
38887     disabled : false,
38888
38889     /**
38890      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38891      */
38892     inputType : undefined,
38893     
38894     /**
38895      * @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).
38896          */
38897         tabIndex : undefined,
38898         
38899     // private
38900     isFormField : true,
38901
38902     // private
38903     hasFocus : false,
38904     /**
38905      * @property {Roo.Element} fieldEl
38906      * Element Containing the rendered Field (with label etc.)
38907      */
38908     /**
38909      * @cfg {Mixed} value A value to initialize this field with.
38910      */
38911     value : undefined,
38912
38913     /**
38914      * @cfg {String} name The field's HTML name attribute.
38915      */
38916     /**
38917      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38918      */
38919     // private
38920     loadedValue : false,
38921      
38922      
38923         // private ??
38924         initComponent : function(){
38925         Roo.form.Field.superclass.initComponent.call(this);
38926         this.addEvents({
38927             /**
38928              * @event focus
38929              * Fires when this field receives input focus.
38930              * @param {Roo.form.Field} this
38931              */
38932             focus : true,
38933             /**
38934              * @event blur
38935              * Fires when this field loses input focus.
38936              * @param {Roo.form.Field} this
38937              */
38938             blur : true,
38939             /**
38940              * @event specialkey
38941              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38942              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38943              * @param {Roo.form.Field} this
38944              * @param {Roo.EventObject} e The event object
38945              */
38946             specialkey : true,
38947             /**
38948              * @event change
38949              * Fires just before the field blurs if the field value has changed.
38950              * @param {Roo.form.Field} this
38951              * @param {Mixed} newValue The new value
38952              * @param {Mixed} oldValue The original value
38953              */
38954             change : true,
38955             /**
38956              * @event invalid
38957              * Fires after the field has been marked as invalid.
38958              * @param {Roo.form.Field} this
38959              * @param {String} msg The validation message
38960              */
38961             invalid : true,
38962             /**
38963              * @event valid
38964              * Fires after the field has been validated with no errors.
38965              * @param {Roo.form.Field} this
38966              */
38967             valid : true,
38968              /**
38969              * @event keyup
38970              * Fires after the key up
38971              * @param {Roo.form.Field} this
38972              * @param {Roo.EventObject}  e The event Object
38973              */
38974             keyup : true
38975         });
38976     },
38977
38978     /**
38979      * Returns the name attribute of the field if available
38980      * @return {String} name The field name
38981      */
38982     getName: function(){
38983          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38984     },
38985
38986     // private
38987     onRender : function(ct, position){
38988         Roo.form.Field.superclass.onRender.call(this, ct, position);
38989         if(!this.el){
38990             var cfg = this.getAutoCreate();
38991             if(!cfg.name){
38992                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38993             }
38994             if (!cfg.name.length) {
38995                 delete cfg.name;
38996             }
38997             if(this.inputType){
38998                 cfg.type = this.inputType;
38999             }
39000             this.el = ct.createChild(cfg, position);
39001         }
39002         var type = this.el.dom.type;
39003         if(type){
39004             if(type == 'password'){
39005                 type = 'text';
39006             }
39007             this.el.addClass('x-form-'+type);
39008         }
39009         if(this.readOnly){
39010             this.el.dom.readOnly = true;
39011         }
39012         if(this.tabIndex !== undefined){
39013             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39014         }
39015
39016         this.el.addClass([this.fieldClass, this.cls]);
39017         this.initValue();
39018     },
39019
39020     /**
39021      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39022      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39023      * @return {Roo.form.Field} this
39024      */
39025     applyTo : function(target){
39026         this.allowDomMove = false;
39027         this.el = Roo.get(target);
39028         this.render(this.el.dom.parentNode);
39029         return this;
39030     },
39031
39032     // private
39033     initValue : function(){
39034         if(this.value !== undefined){
39035             this.setValue(this.value);
39036         }else if(this.el.dom.value.length > 0){
39037             this.setValue(this.el.dom.value);
39038         }
39039     },
39040
39041     /**
39042      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39043      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39044      */
39045     isDirty : function() {
39046         if(this.disabled) {
39047             return false;
39048         }
39049         return String(this.getValue()) !== String(this.originalValue);
39050     },
39051
39052     /**
39053      * stores the current value in loadedValue
39054      */
39055     resetHasChanged : function()
39056     {
39057         this.loadedValue = String(this.getValue());
39058     },
39059     /**
39060      * checks the current value against the 'loaded' value.
39061      * Note - will return false if 'resetHasChanged' has not been called first.
39062      */
39063     hasChanged : function()
39064     {
39065         if(this.disabled || this.readOnly) {
39066             return false;
39067         }
39068         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39069     },
39070     
39071     
39072     
39073     // private
39074     afterRender : function(){
39075         Roo.form.Field.superclass.afterRender.call(this);
39076         this.initEvents();
39077     },
39078
39079     // private
39080     fireKey : function(e){
39081         //Roo.log('field ' + e.getKey());
39082         if(e.isNavKeyPress()){
39083             this.fireEvent("specialkey", this, e);
39084         }
39085     },
39086
39087     /**
39088      * Resets the current field value to the originally loaded value and clears any validation messages
39089      */
39090     reset : function(){
39091         this.setValue(this.resetValue);
39092         this.originalValue = this.getValue();
39093         this.clearInvalid();
39094     },
39095
39096     // private
39097     initEvents : function(){
39098         // safari killled keypress - so keydown is now used..
39099         this.el.on("keydown" , this.fireKey,  this);
39100         this.el.on("focus", this.onFocus,  this);
39101         this.el.on("blur", this.onBlur,  this);
39102         this.el.relayEvent('keyup', this);
39103
39104         // reference to original value for reset
39105         this.originalValue = this.getValue();
39106         this.resetValue =  this.getValue();
39107     },
39108
39109     // private
39110     onFocus : function(){
39111         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39112             this.el.addClass(this.focusClass);
39113         }
39114         if(!this.hasFocus){
39115             this.hasFocus = true;
39116             this.startValue = this.getValue();
39117             this.fireEvent("focus", this);
39118         }
39119     },
39120
39121     beforeBlur : Roo.emptyFn,
39122
39123     // private
39124     onBlur : function(){
39125         this.beforeBlur();
39126         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39127             this.el.removeClass(this.focusClass);
39128         }
39129         this.hasFocus = false;
39130         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39131             this.validate();
39132         }
39133         var v = this.getValue();
39134         if(String(v) !== String(this.startValue)){
39135             this.fireEvent('change', this, v, this.startValue);
39136         }
39137         this.fireEvent("blur", this);
39138     },
39139
39140     /**
39141      * Returns whether or not the field value is currently valid
39142      * @param {Boolean} preventMark True to disable marking the field invalid
39143      * @return {Boolean} True if the value is valid, else false
39144      */
39145     isValid : function(preventMark){
39146         if(this.disabled){
39147             return true;
39148         }
39149         var restore = this.preventMark;
39150         this.preventMark = preventMark === true;
39151         var v = this.validateValue(this.processValue(this.getRawValue()));
39152         this.preventMark = restore;
39153         return v;
39154     },
39155
39156     /**
39157      * Validates the field value
39158      * @return {Boolean} True if the value is valid, else false
39159      */
39160     validate : function(){
39161         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39162             this.clearInvalid();
39163             return true;
39164         }
39165         return false;
39166     },
39167
39168     processValue : function(value){
39169         return value;
39170     },
39171
39172     // private
39173     // Subclasses should provide the validation implementation by overriding this
39174     validateValue : function(value){
39175         return true;
39176     },
39177
39178     /**
39179      * Mark this field as invalid
39180      * @param {String} msg The validation message
39181      */
39182     markInvalid : function(msg){
39183         if(!this.rendered || this.preventMark){ // not rendered
39184             return;
39185         }
39186         
39187         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39188         
39189         obj.el.addClass(this.invalidClass);
39190         msg = msg || this.invalidText;
39191         switch(this.msgTarget){
39192             case 'qtip':
39193                 obj.el.dom.qtip = msg;
39194                 obj.el.dom.qclass = 'x-form-invalid-tip';
39195                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39196                     Roo.QuickTips.enable();
39197                 }
39198                 break;
39199             case 'title':
39200                 this.el.dom.title = msg;
39201                 break;
39202             case 'under':
39203                 if(!this.errorEl){
39204                     var elp = this.el.findParent('.x-form-element', 5, true);
39205                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39206                     this.errorEl.setWidth(elp.getWidth(true)-20);
39207                 }
39208                 this.errorEl.update(msg);
39209                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39210                 break;
39211             case 'side':
39212                 if(!this.errorIcon){
39213                     var elp = this.el.findParent('.x-form-element', 5, true);
39214                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39215                 }
39216                 this.alignErrorIcon();
39217                 this.errorIcon.dom.qtip = msg;
39218                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39219                 this.errorIcon.show();
39220                 this.on('resize', this.alignErrorIcon, this);
39221                 break;
39222             default:
39223                 var t = Roo.getDom(this.msgTarget);
39224                 t.innerHTML = msg;
39225                 t.style.display = this.msgDisplay;
39226                 break;
39227         }
39228         this.fireEvent('invalid', this, msg);
39229     },
39230
39231     // private
39232     alignErrorIcon : function(){
39233         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39234     },
39235
39236     /**
39237      * Clear any invalid styles/messages for this field
39238      */
39239     clearInvalid : function(){
39240         if(!this.rendered || this.preventMark){ // not rendered
39241             return;
39242         }
39243         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39244         
39245         obj.el.removeClass(this.invalidClass);
39246         switch(this.msgTarget){
39247             case 'qtip':
39248                 obj.el.dom.qtip = '';
39249                 break;
39250             case 'title':
39251                 this.el.dom.title = '';
39252                 break;
39253             case 'under':
39254                 if(this.errorEl){
39255                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39256                 }
39257                 break;
39258             case 'side':
39259                 if(this.errorIcon){
39260                     this.errorIcon.dom.qtip = '';
39261                     this.errorIcon.hide();
39262                     this.un('resize', this.alignErrorIcon, this);
39263                 }
39264                 break;
39265             default:
39266                 var t = Roo.getDom(this.msgTarget);
39267                 t.innerHTML = '';
39268                 t.style.display = 'none';
39269                 break;
39270         }
39271         this.fireEvent('valid', this);
39272     },
39273
39274     /**
39275      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39276      * @return {Mixed} value The field value
39277      */
39278     getRawValue : function(){
39279         var v = this.el.getValue();
39280         
39281         return v;
39282     },
39283
39284     /**
39285      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39286      * @return {Mixed} value The field value
39287      */
39288     getValue : function(){
39289         var v = this.el.getValue();
39290          
39291         return v;
39292     },
39293
39294     /**
39295      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39296      * @param {Mixed} value The value to set
39297      */
39298     setRawValue : function(v){
39299         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39300     },
39301
39302     /**
39303      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39304      * @param {Mixed} value The value to set
39305      */
39306     setValue : function(v){
39307         this.value = v;
39308         if(this.rendered){
39309             this.el.dom.value = (v === null || v === undefined ? '' : v);
39310              this.validate();
39311         }
39312     },
39313
39314     adjustSize : function(w, h){
39315         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39316         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39317         return s;
39318     },
39319
39320     adjustWidth : function(tag, w){
39321         tag = tag.toLowerCase();
39322         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39323             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39324                 if(tag == 'input'){
39325                     return w + 2;
39326                 }
39327                 if(tag == 'textarea'){
39328                     return w-2;
39329                 }
39330             }else if(Roo.isOpera){
39331                 if(tag == 'input'){
39332                     return w + 2;
39333                 }
39334                 if(tag == 'textarea'){
39335                     return w-2;
39336                 }
39337             }
39338         }
39339         return w;
39340     }
39341 });
39342
39343
39344 // anything other than normal should be considered experimental
39345 Roo.form.Field.msgFx = {
39346     normal : {
39347         show: function(msgEl, f){
39348             msgEl.setDisplayed('block');
39349         },
39350
39351         hide : function(msgEl, f){
39352             msgEl.setDisplayed(false).update('');
39353         }
39354     },
39355
39356     slide : {
39357         show: function(msgEl, f){
39358             msgEl.slideIn('t', {stopFx:true});
39359         },
39360
39361         hide : function(msgEl, f){
39362             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39363         }
39364     },
39365
39366     slideRight : {
39367         show: function(msgEl, f){
39368             msgEl.fixDisplay();
39369             msgEl.alignTo(f.el, 'tl-tr');
39370             msgEl.slideIn('l', {stopFx:true});
39371         },
39372
39373         hide : function(msgEl, f){
39374             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39375         }
39376     }
39377 };/*
39378  * Based on:
39379  * Ext JS Library 1.1.1
39380  * Copyright(c) 2006-2007, Ext JS, LLC.
39381  *
39382  * Originally Released Under LGPL - original licence link has changed is not relivant.
39383  *
39384  * Fork - LGPL
39385  * <script type="text/javascript">
39386  */
39387  
39388
39389 /**
39390  * @class Roo.form.TextField
39391  * @extends Roo.form.Field
39392  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39393  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39394  * @constructor
39395  * Creates a new TextField
39396  * @param {Object} config Configuration options
39397  */
39398 Roo.form.TextField = function(config){
39399     Roo.form.TextField.superclass.constructor.call(this, config);
39400     this.addEvents({
39401         /**
39402          * @event autosize
39403          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39404          * according to the default logic, but this event provides a hook for the developer to apply additional
39405          * logic at runtime to resize the field if needed.
39406              * @param {Roo.form.Field} this This text field
39407              * @param {Number} width The new field width
39408              */
39409         autosize : true
39410     });
39411 };
39412
39413 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39414     /**
39415      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39416      */
39417     grow : false,
39418     /**
39419      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39420      */
39421     growMin : 30,
39422     /**
39423      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39424      */
39425     growMax : 800,
39426     /**
39427      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39428      */
39429     vtype : null,
39430     /**
39431      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39432      */
39433     maskRe : null,
39434     /**
39435      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39436      */
39437     disableKeyFilter : false,
39438     /**
39439      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39440      */
39441     allowBlank : true,
39442     /**
39443      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39444      */
39445     minLength : 0,
39446     /**
39447      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39448      */
39449     maxLength : Number.MAX_VALUE,
39450     /**
39451      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39452      */
39453     minLengthText : "The minimum length for this field is {0}",
39454     /**
39455      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39456      */
39457     maxLengthText : "The maximum length for this field is {0}",
39458     /**
39459      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39460      */
39461     selectOnFocus : false,
39462     /**
39463      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39464      */    
39465     allowLeadingSpace : false,
39466     /**
39467      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39468      */
39469     blankText : "This field is required",
39470     /**
39471      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39472      * If available, this function will be called only after the basic validators all return true, and will be passed the
39473      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39474      */
39475     validator : null,
39476     /**
39477      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39478      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39479      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39480      */
39481     regex : null,
39482     /**
39483      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39484      */
39485     regexText : "",
39486     /**
39487      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39488      */
39489     emptyText : null,
39490    
39491
39492     // private
39493     initEvents : function()
39494     {
39495         if (this.emptyText) {
39496             this.el.attr('placeholder', this.emptyText);
39497         }
39498         
39499         Roo.form.TextField.superclass.initEvents.call(this);
39500         if(this.validationEvent == 'keyup'){
39501             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39502             this.el.on('keyup', this.filterValidation, this);
39503         }
39504         else if(this.validationEvent !== false){
39505             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39506         }
39507         
39508         if(this.selectOnFocus){
39509             this.on("focus", this.preFocus, this);
39510         }
39511         if (!this.allowLeadingSpace) {
39512             this.on('blur', this.cleanLeadingSpace, this);
39513         }
39514         
39515         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39516             this.el.on("keypress", this.filterKeys, this);
39517         }
39518         if(this.grow){
39519             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39520             this.el.on("click", this.autoSize,  this);
39521         }
39522         if(this.el.is('input[type=password]') && Roo.isSafari){
39523             this.el.on('keydown', this.SafariOnKeyDown, this);
39524         }
39525     },
39526
39527     processValue : function(value){
39528         if(this.stripCharsRe){
39529             var newValue = value.replace(this.stripCharsRe, '');
39530             if(newValue !== value){
39531                 this.setRawValue(newValue);
39532                 return newValue;
39533             }
39534         }
39535         return value;
39536     },
39537
39538     filterValidation : function(e){
39539         if(!e.isNavKeyPress()){
39540             this.validationTask.delay(this.validationDelay);
39541         }
39542     },
39543
39544     // private
39545     onKeyUp : function(e){
39546         if(!e.isNavKeyPress()){
39547             this.autoSize();
39548         }
39549     },
39550     // private - clean the leading white space
39551     cleanLeadingSpace : function(e)
39552     {
39553         if ( this.inputType == 'file') {
39554             return;
39555         }
39556         
39557         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39558     },
39559     /**
39560      * Resets the current field value to the originally-loaded value and clears any validation messages.
39561      *  
39562      */
39563     reset : function(){
39564         Roo.form.TextField.superclass.reset.call(this);
39565        
39566     }, 
39567     // private
39568     preFocus : function(){
39569         
39570         if(this.selectOnFocus){
39571             this.el.dom.select();
39572         }
39573     },
39574
39575     
39576     // private
39577     filterKeys : function(e){
39578         var k = e.getKey();
39579         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39580             return;
39581         }
39582         var c = e.getCharCode(), cc = String.fromCharCode(c);
39583         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39584             return;
39585         }
39586         if(!this.maskRe.test(cc)){
39587             e.stopEvent();
39588         }
39589     },
39590
39591     setValue : function(v){
39592         
39593         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39594         
39595         this.autoSize();
39596     },
39597
39598     /**
39599      * Validates a value according to the field's validation rules and marks the field as invalid
39600      * if the validation fails
39601      * @param {Mixed} value The value to validate
39602      * @return {Boolean} True if the value is valid, else false
39603      */
39604     validateValue : function(value){
39605         if(value.length < 1)  { // if it's blank
39606              if(this.allowBlank){
39607                 this.clearInvalid();
39608                 return true;
39609              }else{
39610                 this.markInvalid(this.blankText);
39611                 return false;
39612              }
39613         }
39614         if(value.length < this.minLength){
39615             this.markInvalid(String.format(this.minLengthText, this.minLength));
39616             return false;
39617         }
39618         if(value.length > this.maxLength){
39619             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39620             return false;
39621         }
39622         if(this.vtype){
39623             var vt = Roo.form.VTypes;
39624             if(!vt[this.vtype](value, this)){
39625                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39626                 return false;
39627             }
39628         }
39629         if(typeof this.validator == "function"){
39630             var msg = this.validator(value);
39631             if(msg !== true){
39632                 this.markInvalid(msg);
39633                 return false;
39634             }
39635         }
39636         if(this.regex && !this.regex.test(value)){
39637             this.markInvalid(this.regexText);
39638             return false;
39639         }
39640         return true;
39641     },
39642
39643     /**
39644      * Selects text in this field
39645      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39646      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39647      */
39648     selectText : function(start, end){
39649         var v = this.getRawValue();
39650         if(v.length > 0){
39651             start = start === undefined ? 0 : start;
39652             end = end === undefined ? v.length : end;
39653             var d = this.el.dom;
39654             if(d.setSelectionRange){
39655                 d.setSelectionRange(start, end);
39656             }else if(d.createTextRange){
39657                 var range = d.createTextRange();
39658                 range.moveStart("character", start);
39659                 range.moveEnd("character", v.length-end);
39660                 range.select();
39661             }
39662         }
39663     },
39664
39665     /**
39666      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39667      * This only takes effect if grow = true, and fires the autosize event.
39668      */
39669     autoSize : function(){
39670         if(!this.grow || !this.rendered){
39671             return;
39672         }
39673         if(!this.metrics){
39674             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39675         }
39676         var el = this.el;
39677         var v = el.dom.value;
39678         var d = document.createElement('div');
39679         d.appendChild(document.createTextNode(v));
39680         v = d.innerHTML;
39681         d = null;
39682         v += "&#160;";
39683         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39684         this.el.setWidth(w);
39685         this.fireEvent("autosize", this, w);
39686     },
39687     
39688     // private
39689     SafariOnKeyDown : function(event)
39690     {
39691         // this is a workaround for a password hang bug on chrome/ webkit.
39692         
39693         var isSelectAll = false;
39694         
39695         if(this.el.dom.selectionEnd > 0){
39696             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39697         }
39698         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39699             event.preventDefault();
39700             this.setValue('');
39701             return;
39702         }
39703         
39704         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39705             
39706             event.preventDefault();
39707             // this is very hacky as keydown always get's upper case.
39708             
39709             var cc = String.fromCharCode(event.getCharCode());
39710             
39711             
39712             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39713             
39714         }
39715         
39716         
39717     }
39718 });/*
39719  * Based on:
39720  * Ext JS Library 1.1.1
39721  * Copyright(c) 2006-2007, Ext JS, LLC.
39722  *
39723  * Originally Released Under LGPL - original licence link has changed is not relivant.
39724  *
39725  * Fork - LGPL
39726  * <script type="text/javascript">
39727  */
39728  
39729 /**
39730  * @class Roo.form.Hidden
39731  * @extends Roo.form.TextField
39732  * Simple Hidden element used on forms 
39733  * 
39734  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39735  * 
39736  * @constructor
39737  * Creates a new Hidden form element.
39738  * @param {Object} config Configuration options
39739  */
39740
39741
39742
39743 // easy hidden field...
39744 Roo.form.Hidden = function(config){
39745     Roo.form.Hidden.superclass.constructor.call(this, config);
39746 };
39747   
39748 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39749     fieldLabel:      '',
39750     inputType:      'hidden',
39751     width:          50,
39752     allowBlank:     true,
39753     labelSeparator: '',
39754     hidden:         true,
39755     itemCls :       'x-form-item-display-none'
39756
39757
39758 });
39759
39760
39761 /*
39762  * Based on:
39763  * Ext JS Library 1.1.1
39764  * Copyright(c) 2006-2007, Ext JS, LLC.
39765  *
39766  * Originally Released Under LGPL - original licence link has changed is not relivant.
39767  *
39768  * Fork - LGPL
39769  * <script type="text/javascript">
39770  */
39771  
39772 /**
39773  * @class Roo.form.TriggerField
39774  * @extends Roo.form.TextField
39775  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39776  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39777  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39778  * for which you can provide a custom implementation.  For example:
39779  * <pre><code>
39780 var trigger = new Roo.form.TriggerField();
39781 trigger.onTriggerClick = myTriggerFn;
39782 trigger.applyTo('my-field');
39783 </code></pre>
39784  *
39785  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39786  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39787  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39788  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39789  * @constructor
39790  * Create a new TriggerField.
39791  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39792  * to the base TextField)
39793  */
39794 Roo.form.TriggerField = function(config){
39795     this.mimicing = false;
39796     Roo.form.TriggerField.superclass.constructor.call(this, config);
39797 };
39798
39799 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39800     /**
39801      * @cfg {String} triggerClass A CSS class to apply to the trigger
39802      */
39803     /**
39804      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39805      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39806      */
39807     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39808     /**
39809      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39810      */
39811     hideTrigger:false,
39812
39813     /** @cfg {Boolean} grow @hide */
39814     /** @cfg {Number} growMin @hide */
39815     /** @cfg {Number} growMax @hide */
39816
39817     /**
39818      * @hide 
39819      * @method
39820      */
39821     autoSize: Roo.emptyFn,
39822     // private
39823     monitorTab : true,
39824     // private
39825     deferHeight : true,
39826
39827     
39828     actionMode : 'wrap',
39829     // private
39830     onResize : function(w, h){
39831         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39832         if(typeof w == 'number'){
39833             var x = w - this.trigger.getWidth();
39834             this.el.setWidth(this.adjustWidth('input', x));
39835             this.trigger.setStyle('left', x+'px');
39836         }
39837     },
39838
39839     // private
39840     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39841
39842     // private
39843     getResizeEl : function(){
39844         return this.wrap;
39845     },
39846
39847     // private
39848     getPositionEl : function(){
39849         return this.wrap;
39850     },
39851
39852     // private
39853     alignErrorIcon : function(){
39854         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39855     },
39856
39857     // private
39858     onRender : function(ct, position){
39859         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39860         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39861         this.trigger = this.wrap.createChild(this.triggerConfig ||
39862                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39863         if(this.hideTrigger){
39864             this.trigger.setDisplayed(false);
39865         }
39866         this.initTrigger();
39867         if(!this.width){
39868             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39869         }
39870     },
39871
39872     // private
39873     initTrigger : function(){
39874         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39875         this.trigger.addClassOnOver('x-form-trigger-over');
39876         this.trigger.addClassOnClick('x-form-trigger-click');
39877     },
39878
39879     // private
39880     onDestroy : function(){
39881         if(this.trigger){
39882             this.trigger.removeAllListeners();
39883             this.trigger.remove();
39884         }
39885         if(this.wrap){
39886             this.wrap.remove();
39887         }
39888         Roo.form.TriggerField.superclass.onDestroy.call(this);
39889     },
39890
39891     // private
39892     onFocus : function(){
39893         Roo.form.TriggerField.superclass.onFocus.call(this);
39894         if(!this.mimicing){
39895             this.wrap.addClass('x-trigger-wrap-focus');
39896             this.mimicing = true;
39897             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39898             if(this.monitorTab){
39899                 this.el.on("keydown", this.checkTab, this);
39900             }
39901         }
39902     },
39903
39904     // private
39905     checkTab : function(e){
39906         if(e.getKey() == e.TAB){
39907             this.triggerBlur();
39908         }
39909     },
39910
39911     // private
39912     onBlur : function(){
39913         // do nothing
39914     },
39915
39916     // private
39917     mimicBlur : function(e, t){
39918         if(!this.wrap.contains(t) && this.validateBlur()){
39919             this.triggerBlur();
39920         }
39921     },
39922
39923     // private
39924     triggerBlur : function(){
39925         this.mimicing = false;
39926         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39927         if(this.monitorTab){
39928             this.el.un("keydown", this.checkTab, this);
39929         }
39930         this.wrap.removeClass('x-trigger-wrap-focus');
39931         Roo.form.TriggerField.superclass.onBlur.call(this);
39932     },
39933
39934     // private
39935     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39936     validateBlur : function(e, t){
39937         return true;
39938     },
39939
39940     // private
39941     onDisable : function(){
39942         Roo.form.TriggerField.superclass.onDisable.call(this);
39943         if(this.wrap){
39944             this.wrap.addClass('x-item-disabled');
39945         }
39946     },
39947
39948     // private
39949     onEnable : function(){
39950         Roo.form.TriggerField.superclass.onEnable.call(this);
39951         if(this.wrap){
39952             this.wrap.removeClass('x-item-disabled');
39953         }
39954     },
39955
39956     // private
39957     onShow : function(){
39958         var ae = this.getActionEl();
39959         
39960         if(ae){
39961             ae.dom.style.display = '';
39962             ae.dom.style.visibility = 'visible';
39963         }
39964     },
39965
39966     // private
39967     
39968     onHide : function(){
39969         var ae = this.getActionEl();
39970         ae.dom.style.display = 'none';
39971     },
39972
39973     /**
39974      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39975      * by an implementing function.
39976      * @method
39977      * @param {EventObject} e
39978      */
39979     onTriggerClick : Roo.emptyFn
39980 });
39981
39982 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39983 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39984 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39985 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39986     initComponent : function(){
39987         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39988
39989         this.triggerConfig = {
39990             tag:'span', cls:'x-form-twin-triggers', cn:[
39991             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39992             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39993         ]};
39994     },
39995
39996     getTrigger : function(index){
39997         return this.triggers[index];
39998     },
39999
40000     initTrigger : function(){
40001         var ts = this.trigger.select('.x-form-trigger', true);
40002         this.wrap.setStyle('overflow', 'hidden');
40003         var triggerField = this;
40004         ts.each(function(t, all, index){
40005             t.hide = function(){
40006                 var w = triggerField.wrap.getWidth();
40007                 this.dom.style.display = 'none';
40008                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40009             };
40010             t.show = function(){
40011                 var w = triggerField.wrap.getWidth();
40012                 this.dom.style.display = '';
40013                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40014             };
40015             var triggerIndex = 'Trigger'+(index+1);
40016
40017             if(this['hide'+triggerIndex]){
40018                 t.dom.style.display = 'none';
40019             }
40020             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40021             t.addClassOnOver('x-form-trigger-over');
40022             t.addClassOnClick('x-form-trigger-click');
40023         }, this);
40024         this.triggers = ts.elements;
40025     },
40026
40027     onTrigger1Click : Roo.emptyFn,
40028     onTrigger2Click : Roo.emptyFn
40029 });/*
40030  * Based on:
40031  * Ext JS Library 1.1.1
40032  * Copyright(c) 2006-2007, Ext JS, LLC.
40033  *
40034  * Originally Released Under LGPL - original licence link has changed is not relivant.
40035  *
40036  * Fork - LGPL
40037  * <script type="text/javascript">
40038  */
40039  
40040 /**
40041  * @class Roo.form.TextArea
40042  * @extends Roo.form.TextField
40043  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40044  * support for auto-sizing.
40045  * @constructor
40046  * Creates a new TextArea
40047  * @param {Object} config Configuration options
40048  */
40049 Roo.form.TextArea = function(config){
40050     Roo.form.TextArea.superclass.constructor.call(this, config);
40051     // these are provided exchanges for backwards compat
40052     // minHeight/maxHeight were replaced by growMin/growMax to be
40053     // compatible with TextField growing config values
40054     if(this.minHeight !== undefined){
40055         this.growMin = this.minHeight;
40056     }
40057     if(this.maxHeight !== undefined){
40058         this.growMax = this.maxHeight;
40059     }
40060 };
40061
40062 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40063     /**
40064      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40065      */
40066     growMin : 60,
40067     /**
40068      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40069      */
40070     growMax: 1000,
40071     /**
40072      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40073      * in the field (equivalent to setting overflow: hidden, defaults to false)
40074      */
40075     preventScrollbars: false,
40076     /**
40077      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40078      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40079      */
40080
40081     // private
40082     onRender : function(ct, position){
40083         if(!this.el){
40084             this.defaultAutoCreate = {
40085                 tag: "textarea",
40086                 style:"width:300px;height:60px;",
40087                 autocomplete: "new-password"
40088             };
40089         }
40090         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40091         if(this.grow){
40092             this.textSizeEl = Roo.DomHelper.append(document.body, {
40093                 tag: "pre", cls: "x-form-grow-sizer"
40094             });
40095             if(this.preventScrollbars){
40096                 this.el.setStyle("overflow", "hidden");
40097             }
40098             this.el.setHeight(this.growMin);
40099         }
40100     },
40101
40102     onDestroy : function(){
40103         if(this.textSizeEl){
40104             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40105         }
40106         Roo.form.TextArea.superclass.onDestroy.call(this);
40107     },
40108
40109     // private
40110     onKeyUp : function(e){
40111         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40112             this.autoSize();
40113         }
40114     },
40115
40116     /**
40117      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40118      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40119      */
40120     autoSize : function(){
40121         if(!this.grow || !this.textSizeEl){
40122             return;
40123         }
40124         var el = this.el;
40125         var v = el.dom.value;
40126         var ts = this.textSizeEl;
40127
40128         ts.innerHTML = '';
40129         ts.appendChild(document.createTextNode(v));
40130         v = ts.innerHTML;
40131
40132         Roo.fly(ts).setWidth(this.el.getWidth());
40133         if(v.length < 1){
40134             v = "&#160;&#160;";
40135         }else{
40136             if(Roo.isIE){
40137                 v = v.replace(/\n/g, '<p>&#160;</p>');
40138             }
40139             v += "&#160;\n&#160;";
40140         }
40141         ts.innerHTML = v;
40142         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40143         if(h != this.lastHeight){
40144             this.lastHeight = h;
40145             this.el.setHeight(h);
40146             this.fireEvent("autosize", this, h);
40147         }
40148     }
40149 });/*
40150  * Based on:
40151  * Ext JS Library 1.1.1
40152  * Copyright(c) 2006-2007, Ext JS, LLC.
40153  *
40154  * Originally Released Under LGPL - original licence link has changed is not relivant.
40155  *
40156  * Fork - LGPL
40157  * <script type="text/javascript">
40158  */
40159  
40160
40161 /**
40162  * @class Roo.form.NumberField
40163  * @extends Roo.form.TextField
40164  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40165  * @constructor
40166  * Creates a new NumberField
40167  * @param {Object} config Configuration options
40168  */
40169 Roo.form.NumberField = function(config){
40170     Roo.form.NumberField.superclass.constructor.call(this, config);
40171 };
40172
40173 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40174     /**
40175      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40176      */
40177     fieldClass: "x-form-field x-form-num-field",
40178     /**
40179      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40180      */
40181     allowDecimals : true,
40182     /**
40183      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40184      */
40185     decimalSeparator : ".",
40186     /**
40187      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40188      */
40189     decimalPrecision : 2,
40190     /**
40191      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40192      */
40193     allowNegative : true,
40194     /**
40195      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40196      */
40197     minValue : Number.NEGATIVE_INFINITY,
40198     /**
40199      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40200      */
40201     maxValue : Number.MAX_VALUE,
40202     /**
40203      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40204      */
40205     minText : "The minimum value for this field is {0}",
40206     /**
40207      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40208      */
40209     maxText : "The maximum value for this field is {0}",
40210     /**
40211      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40212      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40213      */
40214     nanText : "{0} is not a valid number",
40215
40216     // private
40217     initEvents : function(){
40218         Roo.form.NumberField.superclass.initEvents.call(this);
40219         var allowed = "0123456789";
40220         if(this.allowDecimals){
40221             allowed += this.decimalSeparator;
40222         }
40223         if(this.allowNegative){
40224             allowed += "-";
40225         }
40226         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40227         var keyPress = function(e){
40228             var k = e.getKey();
40229             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40230                 return;
40231             }
40232             var c = e.getCharCode();
40233             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40234                 e.stopEvent();
40235             }
40236         };
40237         this.el.on("keypress", keyPress, this);
40238     },
40239
40240     // private
40241     validateValue : function(value){
40242         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40243             return false;
40244         }
40245         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40246              return true;
40247         }
40248         var num = this.parseValue(value);
40249         if(isNaN(num)){
40250             this.markInvalid(String.format(this.nanText, value));
40251             return false;
40252         }
40253         if(num < this.minValue){
40254             this.markInvalid(String.format(this.minText, this.minValue));
40255             return false;
40256         }
40257         if(num > this.maxValue){
40258             this.markInvalid(String.format(this.maxText, this.maxValue));
40259             return false;
40260         }
40261         return true;
40262     },
40263
40264     getValue : function(){
40265         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40266     },
40267
40268     // private
40269     parseValue : function(value){
40270         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40271         return isNaN(value) ? '' : value;
40272     },
40273
40274     // private
40275     fixPrecision : function(value){
40276         var nan = isNaN(value);
40277         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40278             return nan ? '' : value;
40279         }
40280         return parseFloat(value).toFixed(this.decimalPrecision);
40281     },
40282
40283     setValue : function(v){
40284         v = this.fixPrecision(v);
40285         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40286     },
40287
40288     // private
40289     decimalPrecisionFcn : function(v){
40290         return Math.floor(v);
40291     },
40292
40293     beforeBlur : function(){
40294         var v = this.parseValue(this.getRawValue());
40295         if(v){
40296             this.setValue(v);
40297         }
40298     }
40299 });/*
40300  * Based on:
40301  * Ext JS Library 1.1.1
40302  * Copyright(c) 2006-2007, Ext JS, LLC.
40303  *
40304  * Originally Released Under LGPL - original licence link has changed is not relivant.
40305  *
40306  * Fork - LGPL
40307  * <script type="text/javascript">
40308  */
40309  
40310 /**
40311  * @class Roo.form.DateField
40312  * @extends Roo.form.TriggerField
40313  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40314 * @constructor
40315 * Create a new DateField
40316 * @param {Object} config
40317  */
40318 Roo.form.DateField = function(config)
40319 {
40320     Roo.form.DateField.superclass.constructor.call(this, config);
40321     
40322       this.addEvents({
40323          
40324         /**
40325          * @event select
40326          * Fires when a date is selected
40327              * @param {Roo.form.DateField} combo This combo box
40328              * @param {Date} date The date selected
40329              */
40330         'select' : true
40331          
40332     });
40333     
40334     
40335     if(typeof this.minValue == "string") {
40336         this.minValue = this.parseDate(this.minValue);
40337     }
40338     if(typeof this.maxValue == "string") {
40339         this.maxValue = this.parseDate(this.maxValue);
40340     }
40341     this.ddMatch = null;
40342     if(this.disabledDates){
40343         var dd = this.disabledDates;
40344         var re = "(?:";
40345         for(var i = 0; i < dd.length; i++){
40346             re += dd[i];
40347             if(i != dd.length-1) {
40348                 re += "|";
40349             }
40350         }
40351         this.ddMatch = new RegExp(re + ")");
40352     }
40353 };
40354
40355 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40356     /**
40357      * @cfg {String} format
40358      * The default date format string which can be overriden for localization support.  The format must be
40359      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40360      */
40361     format : "m/d/y",
40362     /**
40363      * @cfg {String} altFormats
40364      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40365      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40366      */
40367     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40368     /**
40369      * @cfg {Array} disabledDays
40370      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40371      */
40372     disabledDays : null,
40373     /**
40374      * @cfg {String} disabledDaysText
40375      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40376      */
40377     disabledDaysText : "Disabled",
40378     /**
40379      * @cfg {Array} disabledDates
40380      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40381      * expression so they are very powerful. Some examples:
40382      * <ul>
40383      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40384      * <li>["03/08", "09/16"] would disable those days for every year</li>
40385      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40386      * <li>["03/../2006"] would disable every day in March 2006</li>
40387      * <li>["^03"] would disable every day in every March</li>
40388      * </ul>
40389      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40390      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40391      */
40392     disabledDates : null,
40393     /**
40394      * @cfg {String} disabledDatesText
40395      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40396      */
40397     disabledDatesText : "Disabled",
40398     /**
40399      * @cfg {Date/String} minValue
40400      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40401      * valid format (defaults to null).
40402      */
40403     minValue : null,
40404     /**
40405      * @cfg {Date/String} maxValue
40406      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40407      * valid format (defaults to null).
40408      */
40409     maxValue : null,
40410     /**
40411      * @cfg {String} minText
40412      * The error text to display when the date in the cell is before minValue (defaults to
40413      * 'The date in this field must be after {minValue}').
40414      */
40415     minText : "The date in this field must be equal to or after {0}",
40416     /**
40417      * @cfg {String} maxText
40418      * The error text to display when the date in the cell is after maxValue (defaults to
40419      * 'The date in this field must be before {maxValue}').
40420      */
40421     maxText : "The date in this field must be equal to or before {0}",
40422     /**
40423      * @cfg {String} invalidText
40424      * The error text to display when the date in the field is invalid (defaults to
40425      * '{value} is not a valid date - it must be in the format {format}').
40426      */
40427     invalidText : "{0} is not a valid date - it must be in the format {1}",
40428     /**
40429      * @cfg {String} triggerClass
40430      * An additional CSS class used to style the trigger button.  The trigger will always get the
40431      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40432      * which displays a calendar icon).
40433      */
40434     triggerClass : 'x-form-date-trigger',
40435     
40436
40437     /**
40438      * @cfg {Boolean} useIso
40439      * if enabled, then the date field will use a hidden field to store the 
40440      * real value as iso formated date. default (false)
40441      */ 
40442     useIso : false,
40443     /**
40444      * @cfg {String/Object} autoCreate
40445      * A DomHelper element spec, or true for a default element spec (defaults to
40446      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40447      */ 
40448     // private
40449     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40450     
40451     // private
40452     hiddenField: false,
40453     
40454     onRender : function(ct, position)
40455     {
40456         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40457         if (this.useIso) {
40458             //this.el.dom.removeAttribute('name'); 
40459             Roo.log("Changing name?");
40460             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40461             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40462                     'before', true);
40463             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40464             // prevent input submission
40465             this.hiddenName = this.name;
40466         }
40467             
40468             
40469     },
40470     
40471     // private
40472     validateValue : function(value)
40473     {
40474         value = this.formatDate(value);
40475         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40476             Roo.log('super failed');
40477             return false;
40478         }
40479         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40480              return true;
40481         }
40482         var svalue = value;
40483         value = this.parseDate(value);
40484         if(!value){
40485             Roo.log('parse date failed' + svalue);
40486             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40487             return false;
40488         }
40489         var time = value.getTime();
40490         if(this.minValue && time < this.minValue.getTime()){
40491             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40492             return false;
40493         }
40494         if(this.maxValue && time > this.maxValue.getTime()){
40495             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40496             return false;
40497         }
40498         if(this.disabledDays){
40499             var day = value.getDay();
40500             for(var i = 0; i < this.disabledDays.length; i++) {
40501                 if(day === this.disabledDays[i]){
40502                     this.markInvalid(this.disabledDaysText);
40503                     return false;
40504                 }
40505             }
40506         }
40507         var fvalue = this.formatDate(value);
40508         if(this.ddMatch && this.ddMatch.test(fvalue)){
40509             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40510             return false;
40511         }
40512         return true;
40513     },
40514
40515     // private
40516     // Provides logic to override the default TriggerField.validateBlur which just returns true
40517     validateBlur : function(){
40518         return !this.menu || !this.menu.isVisible();
40519     },
40520     
40521     getName: function()
40522     {
40523         // returns hidden if it's set..
40524         if (!this.rendered) {return ''};
40525         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40526         
40527     },
40528
40529     /**
40530      * Returns the current date value of the date field.
40531      * @return {Date} The date value
40532      */
40533     getValue : function(){
40534         
40535         return  this.hiddenField ?
40536                 this.hiddenField.value :
40537                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40538     },
40539
40540     /**
40541      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40542      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40543      * (the default format used is "m/d/y").
40544      * <br />Usage:
40545      * <pre><code>
40546 //All of these calls set the same date value (May 4, 2006)
40547
40548 //Pass a date object:
40549 var dt = new Date('5/4/06');
40550 dateField.setValue(dt);
40551
40552 //Pass a date string (default format):
40553 dateField.setValue('5/4/06');
40554
40555 //Pass a date string (custom format):
40556 dateField.format = 'Y-m-d';
40557 dateField.setValue('2006-5-4');
40558 </code></pre>
40559      * @param {String/Date} date The date or valid date string
40560      */
40561     setValue : function(date){
40562         if (this.hiddenField) {
40563             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40564         }
40565         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40566         // make sure the value field is always stored as a date..
40567         this.value = this.parseDate(date);
40568         
40569         
40570     },
40571
40572     // private
40573     parseDate : function(value){
40574         if(!value || value instanceof Date){
40575             return value;
40576         }
40577         var v = Date.parseDate(value, this.format);
40578          if (!v && this.useIso) {
40579             v = Date.parseDate(value, 'Y-m-d');
40580         }
40581         if(!v && this.altFormats){
40582             if(!this.altFormatsArray){
40583                 this.altFormatsArray = this.altFormats.split("|");
40584             }
40585             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40586                 v = Date.parseDate(value, this.altFormatsArray[i]);
40587             }
40588         }
40589         return v;
40590     },
40591
40592     // private
40593     formatDate : function(date, fmt){
40594         return (!date || !(date instanceof Date)) ?
40595                date : date.dateFormat(fmt || this.format);
40596     },
40597
40598     // private
40599     menuListeners : {
40600         select: function(m, d){
40601             
40602             this.setValue(d);
40603             this.fireEvent('select', this, d);
40604         },
40605         show : function(){ // retain focus styling
40606             this.onFocus();
40607         },
40608         hide : function(){
40609             this.focus.defer(10, this);
40610             var ml = this.menuListeners;
40611             this.menu.un("select", ml.select,  this);
40612             this.menu.un("show", ml.show,  this);
40613             this.menu.un("hide", ml.hide,  this);
40614         }
40615     },
40616
40617     // private
40618     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40619     onTriggerClick : function(){
40620         if(this.disabled){
40621             return;
40622         }
40623         if(this.menu == null){
40624             this.menu = new Roo.menu.DateMenu();
40625         }
40626         Roo.apply(this.menu.picker,  {
40627             showClear: this.allowBlank,
40628             minDate : this.minValue,
40629             maxDate : this.maxValue,
40630             disabledDatesRE : this.ddMatch,
40631             disabledDatesText : this.disabledDatesText,
40632             disabledDays : this.disabledDays,
40633             disabledDaysText : this.disabledDaysText,
40634             format : this.useIso ? 'Y-m-d' : this.format,
40635             minText : String.format(this.minText, this.formatDate(this.minValue)),
40636             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40637         });
40638         this.menu.on(Roo.apply({}, this.menuListeners, {
40639             scope:this
40640         }));
40641         this.menu.picker.setValue(this.getValue() || new Date());
40642         this.menu.show(this.el, "tl-bl?");
40643     },
40644
40645     beforeBlur : function(){
40646         var v = this.parseDate(this.getRawValue());
40647         if(v){
40648             this.setValue(v);
40649         }
40650     },
40651
40652     /*@
40653      * overide
40654      * 
40655      */
40656     isDirty : function() {
40657         if(this.disabled) {
40658             return false;
40659         }
40660         
40661         if(typeof(this.startValue) === 'undefined'){
40662             return false;
40663         }
40664         
40665         return String(this.getValue()) !== String(this.startValue);
40666         
40667     },
40668     // @overide
40669     cleanLeadingSpace : function(e)
40670     {
40671        return;
40672     }
40673     
40674 });/*
40675  * Based on:
40676  * Ext JS Library 1.1.1
40677  * Copyright(c) 2006-2007, Ext JS, LLC.
40678  *
40679  * Originally Released Under LGPL - original licence link has changed is not relivant.
40680  *
40681  * Fork - LGPL
40682  * <script type="text/javascript">
40683  */
40684  
40685 /**
40686  * @class Roo.form.MonthField
40687  * @extends Roo.form.TriggerField
40688  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40689 * @constructor
40690 * Create a new MonthField
40691 * @param {Object} config
40692  */
40693 Roo.form.MonthField = function(config){
40694     
40695     Roo.form.MonthField.superclass.constructor.call(this, config);
40696     
40697       this.addEvents({
40698          
40699         /**
40700          * @event select
40701          * Fires when a date is selected
40702              * @param {Roo.form.MonthFieeld} combo This combo box
40703              * @param {Date} date The date selected
40704              */
40705         'select' : true
40706          
40707     });
40708     
40709     
40710     if(typeof this.minValue == "string") {
40711         this.minValue = this.parseDate(this.minValue);
40712     }
40713     if(typeof this.maxValue == "string") {
40714         this.maxValue = this.parseDate(this.maxValue);
40715     }
40716     this.ddMatch = null;
40717     if(this.disabledDates){
40718         var dd = this.disabledDates;
40719         var re = "(?:";
40720         for(var i = 0; i < dd.length; i++){
40721             re += dd[i];
40722             if(i != dd.length-1) {
40723                 re += "|";
40724             }
40725         }
40726         this.ddMatch = new RegExp(re + ")");
40727     }
40728 };
40729
40730 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40731     /**
40732      * @cfg {String} format
40733      * The default date format string which can be overriden for localization support.  The format must be
40734      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40735      */
40736     format : "M Y",
40737     /**
40738      * @cfg {String} altFormats
40739      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40740      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40741      */
40742     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40743     /**
40744      * @cfg {Array} disabledDays
40745      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40746      */
40747     disabledDays : [0,1,2,3,4,5,6],
40748     /**
40749      * @cfg {String} disabledDaysText
40750      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40751      */
40752     disabledDaysText : "Disabled",
40753     /**
40754      * @cfg {Array} disabledDates
40755      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40756      * expression so they are very powerful. Some examples:
40757      * <ul>
40758      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40759      * <li>["03/08", "09/16"] would disable those days for every year</li>
40760      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40761      * <li>["03/../2006"] would disable every day in March 2006</li>
40762      * <li>["^03"] would disable every day in every March</li>
40763      * </ul>
40764      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40765      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40766      */
40767     disabledDates : null,
40768     /**
40769      * @cfg {String} disabledDatesText
40770      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40771      */
40772     disabledDatesText : "Disabled",
40773     /**
40774      * @cfg {Date/String} minValue
40775      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40776      * valid format (defaults to null).
40777      */
40778     minValue : null,
40779     /**
40780      * @cfg {Date/String} maxValue
40781      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40782      * valid format (defaults to null).
40783      */
40784     maxValue : null,
40785     /**
40786      * @cfg {String} minText
40787      * The error text to display when the date in the cell is before minValue (defaults to
40788      * 'The date in this field must be after {minValue}').
40789      */
40790     minText : "The date in this field must be equal to or after {0}",
40791     /**
40792      * @cfg {String} maxTextf
40793      * The error text to display when the date in the cell is after maxValue (defaults to
40794      * 'The date in this field must be before {maxValue}').
40795      */
40796     maxText : "The date in this field must be equal to or before {0}",
40797     /**
40798      * @cfg {String} invalidText
40799      * The error text to display when the date in the field is invalid (defaults to
40800      * '{value} is not a valid date - it must be in the format {format}').
40801      */
40802     invalidText : "{0} is not a valid date - it must be in the format {1}",
40803     /**
40804      * @cfg {String} triggerClass
40805      * An additional CSS class used to style the trigger button.  The trigger will always get the
40806      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40807      * which displays a calendar icon).
40808      */
40809     triggerClass : 'x-form-date-trigger',
40810     
40811
40812     /**
40813      * @cfg {Boolean} useIso
40814      * if enabled, then the date field will use a hidden field to store the 
40815      * real value as iso formated date. default (true)
40816      */ 
40817     useIso : true,
40818     /**
40819      * @cfg {String/Object} autoCreate
40820      * A DomHelper element spec, or true for a default element spec (defaults to
40821      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40822      */ 
40823     // private
40824     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40825     
40826     // private
40827     hiddenField: false,
40828     
40829     hideMonthPicker : false,
40830     
40831     onRender : function(ct, position)
40832     {
40833         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40834         if (this.useIso) {
40835             this.el.dom.removeAttribute('name'); 
40836             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40837                     'before', true);
40838             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40839             // prevent input submission
40840             this.hiddenName = this.name;
40841         }
40842             
40843             
40844     },
40845     
40846     // private
40847     validateValue : function(value)
40848     {
40849         value = this.formatDate(value);
40850         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40851             return false;
40852         }
40853         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40854              return true;
40855         }
40856         var svalue = value;
40857         value = this.parseDate(value);
40858         if(!value){
40859             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40860             return false;
40861         }
40862         var time = value.getTime();
40863         if(this.minValue && time < this.minValue.getTime()){
40864             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40865             return false;
40866         }
40867         if(this.maxValue && time > this.maxValue.getTime()){
40868             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40869             return false;
40870         }
40871         /*if(this.disabledDays){
40872             var day = value.getDay();
40873             for(var i = 0; i < this.disabledDays.length; i++) {
40874                 if(day === this.disabledDays[i]){
40875                     this.markInvalid(this.disabledDaysText);
40876                     return false;
40877                 }
40878             }
40879         }
40880         */
40881         var fvalue = this.formatDate(value);
40882         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40883             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40884             return false;
40885         }
40886         */
40887         return true;
40888     },
40889
40890     // private
40891     // Provides logic to override the default TriggerField.validateBlur which just returns true
40892     validateBlur : function(){
40893         return !this.menu || !this.menu.isVisible();
40894     },
40895
40896     /**
40897      * Returns the current date value of the date field.
40898      * @return {Date} The date value
40899      */
40900     getValue : function(){
40901         
40902         
40903         
40904         return  this.hiddenField ?
40905                 this.hiddenField.value :
40906                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40907     },
40908
40909     /**
40910      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40911      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40912      * (the default format used is "m/d/y").
40913      * <br />Usage:
40914      * <pre><code>
40915 //All of these calls set the same date value (May 4, 2006)
40916
40917 //Pass a date object:
40918 var dt = new Date('5/4/06');
40919 monthField.setValue(dt);
40920
40921 //Pass a date string (default format):
40922 monthField.setValue('5/4/06');
40923
40924 //Pass a date string (custom format):
40925 monthField.format = 'Y-m-d';
40926 monthField.setValue('2006-5-4');
40927 </code></pre>
40928      * @param {String/Date} date The date or valid date string
40929      */
40930     setValue : function(date){
40931         Roo.log('month setValue' + date);
40932         // can only be first of month..
40933         
40934         var val = this.parseDate(date);
40935         
40936         if (this.hiddenField) {
40937             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40938         }
40939         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40940         this.value = this.parseDate(date);
40941     },
40942
40943     // private
40944     parseDate : function(value){
40945         if(!value || value instanceof Date){
40946             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40947             return value;
40948         }
40949         var v = Date.parseDate(value, this.format);
40950         if (!v && this.useIso) {
40951             v = Date.parseDate(value, 'Y-m-d');
40952         }
40953         if (v) {
40954             // 
40955             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40956         }
40957         
40958         
40959         if(!v && this.altFormats){
40960             if(!this.altFormatsArray){
40961                 this.altFormatsArray = this.altFormats.split("|");
40962             }
40963             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40964                 v = Date.parseDate(value, this.altFormatsArray[i]);
40965             }
40966         }
40967         return v;
40968     },
40969
40970     // private
40971     formatDate : function(date, fmt){
40972         return (!date || !(date instanceof Date)) ?
40973                date : date.dateFormat(fmt || this.format);
40974     },
40975
40976     // private
40977     menuListeners : {
40978         select: function(m, d){
40979             this.setValue(d);
40980             this.fireEvent('select', this, d);
40981         },
40982         show : function(){ // retain focus styling
40983             this.onFocus();
40984         },
40985         hide : function(){
40986             this.focus.defer(10, this);
40987             var ml = this.menuListeners;
40988             this.menu.un("select", ml.select,  this);
40989             this.menu.un("show", ml.show,  this);
40990             this.menu.un("hide", ml.hide,  this);
40991         }
40992     },
40993     // private
40994     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40995     onTriggerClick : function(){
40996         if(this.disabled){
40997             return;
40998         }
40999         if(this.menu == null){
41000             this.menu = new Roo.menu.DateMenu();
41001            
41002         }
41003         
41004         Roo.apply(this.menu.picker,  {
41005             
41006             showClear: this.allowBlank,
41007             minDate : this.minValue,
41008             maxDate : this.maxValue,
41009             disabledDatesRE : this.ddMatch,
41010             disabledDatesText : this.disabledDatesText,
41011             
41012             format : this.useIso ? 'Y-m-d' : this.format,
41013             minText : String.format(this.minText, this.formatDate(this.minValue)),
41014             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41015             
41016         });
41017          this.menu.on(Roo.apply({}, this.menuListeners, {
41018             scope:this
41019         }));
41020        
41021         
41022         var m = this.menu;
41023         var p = m.picker;
41024         
41025         // hide month picker get's called when we called by 'before hide';
41026         
41027         var ignorehide = true;
41028         p.hideMonthPicker  = function(disableAnim){
41029             if (ignorehide) {
41030                 return;
41031             }
41032              if(this.monthPicker){
41033                 Roo.log("hideMonthPicker called");
41034                 if(disableAnim === true){
41035                     this.monthPicker.hide();
41036                 }else{
41037                     this.monthPicker.slideOut('t', {duration:.2});
41038                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41039                     p.fireEvent("select", this, this.value);
41040                     m.hide();
41041                 }
41042             }
41043         }
41044         
41045         Roo.log('picker set value');
41046         Roo.log(this.getValue());
41047         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41048         m.show(this.el, 'tl-bl?');
41049         ignorehide  = false;
41050         // this will trigger hideMonthPicker..
41051         
41052         
41053         // hidden the day picker
41054         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41055         
41056         
41057         
41058       
41059         
41060         p.showMonthPicker.defer(100, p);
41061     
41062         
41063        
41064     },
41065
41066     beforeBlur : function(){
41067         var v = this.parseDate(this.getRawValue());
41068         if(v){
41069             this.setValue(v);
41070         }
41071     }
41072
41073     /** @cfg {Boolean} grow @hide */
41074     /** @cfg {Number} growMin @hide */
41075     /** @cfg {Number} growMax @hide */
41076     /**
41077      * @hide
41078      * @method autoSize
41079      */
41080 });/*
41081  * Based on:
41082  * Ext JS Library 1.1.1
41083  * Copyright(c) 2006-2007, Ext JS, LLC.
41084  *
41085  * Originally Released Under LGPL - original licence link has changed is not relivant.
41086  *
41087  * Fork - LGPL
41088  * <script type="text/javascript">
41089  */
41090  
41091
41092 /**
41093  * @class Roo.form.ComboBox
41094  * @extends Roo.form.TriggerField
41095  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41096  * @constructor
41097  * Create a new ComboBox.
41098  * @param {Object} config Configuration options
41099  */
41100 Roo.form.ComboBox = function(config){
41101     Roo.form.ComboBox.superclass.constructor.call(this, config);
41102     this.addEvents({
41103         /**
41104          * @event expand
41105          * Fires when the dropdown list is expanded
41106              * @param {Roo.form.ComboBox} combo This combo box
41107              */
41108         'expand' : true,
41109         /**
41110          * @event collapse
41111          * Fires when the dropdown list is collapsed
41112              * @param {Roo.form.ComboBox} combo This combo box
41113              */
41114         'collapse' : true,
41115         /**
41116          * @event beforeselect
41117          * Fires before a list item is selected. Return false to cancel the selection.
41118              * @param {Roo.form.ComboBox} combo This combo box
41119              * @param {Roo.data.Record} record The data record returned from the underlying store
41120              * @param {Number} index The index of the selected item in the dropdown list
41121              */
41122         'beforeselect' : true,
41123         /**
41124          * @event select
41125          * Fires when a list item is selected
41126              * @param {Roo.form.ComboBox} combo This combo box
41127              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41128              * @param {Number} index The index of the selected item in the dropdown list
41129              */
41130         'select' : true,
41131         /**
41132          * @event beforequery
41133          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41134          * The event object passed has these properties:
41135              * @param {Roo.form.ComboBox} combo This combo box
41136              * @param {String} query The query
41137              * @param {Boolean} forceAll true to force "all" query
41138              * @param {Boolean} cancel true to cancel the query
41139              * @param {Object} e The query event object
41140              */
41141         'beforequery': true,
41142          /**
41143          * @event add
41144          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41145              * @param {Roo.form.ComboBox} combo This combo box
41146              */
41147         'add' : true,
41148         /**
41149          * @event edit
41150          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41151              * @param {Roo.form.ComboBox} combo This combo box
41152              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41153              */
41154         'edit' : true
41155         
41156         
41157     });
41158     if(this.transform){
41159         this.allowDomMove = false;
41160         var s = Roo.getDom(this.transform);
41161         if(!this.hiddenName){
41162             this.hiddenName = s.name;
41163         }
41164         if(!this.store){
41165             this.mode = 'local';
41166             var d = [], opts = s.options;
41167             for(var i = 0, len = opts.length;i < len; i++){
41168                 var o = opts[i];
41169                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41170                 if(o.selected) {
41171                     this.value = value;
41172                 }
41173                 d.push([value, o.text]);
41174             }
41175             this.store = new Roo.data.SimpleStore({
41176                 'id': 0,
41177                 fields: ['value', 'text'],
41178                 data : d
41179             });
41180             this.valueField = 'value';
41181             this.displayField = 'text';
41182         }
41183         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41184         if(!this.lazyRender){
41185             this.target = true;
41186             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41187             s.parentNode.removeChild(s); // remove it
41188             this.render(this.el.parentNode);
41189         }else{
41190             s.parentNode.removeChild(s); // remove it
41191         }
41192
41193     }
41194     if (this.store) {
41195         this.store = Roo.factory(this.store, Roo.data);
41196     }
41197     
41198     this.selectedIndex = -1;
41199     if(this.mode == 'local'){
41200         if(config.queryDelay === undefined){
41201             this.queryDelay = 10;
41202         }
41203         if(config.minChars === undefined){
41204             this.minChars = 0;
41205         }
41206     }
41207 };
41208
41209 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41210     /**
41211      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41212      */
41213     /**
41214      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41215      * rendering into an Roo.Editor, defaults to false)
41216      */
41217     /**
41218      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41219      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41220      */
41221     /**
41222      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41223      */
41224     /**
41225      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41226      * the dropdown list (defaults to undefined, with no header element)
41227      */
41228
41229      /**
41230      * @cfg {String/Roo.Template} tpl The template to use to render the output
41231      */
41232      
41233     // private
41234     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41235     /**
41236      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41237      */
41238     listWidth: undefined,
41239     /**
41240      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41241      * mode = 'remote' or 'text' if mode = 'local')
41242      */
41243     displayField: undefined,
41244     /**
41245      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41246      * mode = 'remote' or 'value' if mode = 'local'). 
41247      * Note: use of a valueField requires the user make a selection
41248      * in order for a value to be mapped.
41249      */
41250     valueField: undefined,
41251     
41252     
41253     /**
41254      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41255      * field's data value (defaults to the underlying DOM element's name)
41256      */
41257     hiddenName: undefined,
41258     /**
41259      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41260      */
41261     listClass: '',
41262     /**
41263      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41264      */
41265     selectedClass: 'x-combo-selected',
41266     /**
41267      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41268      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41269      * which displays a downward arrow icon).
41270      */
41271     triggerClass : 'x-form-arrow-trigger',
41272     /**
41273      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41274      */
41275     shadow:'sides',
41276     /**
41277      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41278      * anchor positions (defaults to 'tl-bl')
41279      */
41280     listAlign: 'tl-bl?',
41281     /**
41282      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41283      */
41284     maxHeight: 300,
41285     /**
41286      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41287      * query specified by the allQuery config option (defaults to 'query')
41288      */
41289     triggerAction: 'query',
41290     /**
41291      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41292      * (defaults to 4, does not apply if editable = false)
41293      */
41294     minChars : 4,
41295     /**
41296      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41297      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41298      */
41299     typeAhead: false,
41300     /**
41301      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41302      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41303      */
41304     queryDelay: 500,
41305     /**
41306      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41307      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41308      */
41309     pageSize: 0,
41310     /**
41311      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41312      * when editable = true (defaults to false)
41313      */
41314     selectOnFocus:false,
41315     /**
41316      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41317      */
41318     queryParam: 'query',
41319     /**
41320      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41321      * when mode = 'remote' (defaults to 'Loading...')
41322      */
41323     loadingText: 'Loading...',
41324     /**
41325      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41326      */
41327     resizable: false,
41328     /**
41329      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41330      */
41331     handleHeight : 8,
41332     /**
41333      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41334      * traditional select (defaults to true)
41335      */
41336     editable: true,
41337     /**
41338      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41339      */
41340     allQuery: '',
41341     /**
41342      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41343      */
41344     mode: 'remote',
41345     /**
41346      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41347      * listWidth has a higher value)
41348      */
41349     minListWidth : 70,
41350     /**
41351      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41352      * allow the user to set arbitrary text into the field (defaults to false)
41353      */
41354     forceSelection:false,
41355     /**
41356      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41357      * if typeAhead = true (defaults to 250)
41358      */
41359     typeAheadDelay : 250,
41360     /**
41361      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41362      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41363      */
41364     valueNotFoundText : undefined,
41365     /**
41366      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41367      */
41368     blockFocus : false,
41369     
41370     /**
41371      * @cfg {Boolean} disableClear Disable showing of clear button.
41372      */
41373     disableClear : false,
41374     /**
41375      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41376      */
41377     alwaysQuery : false,
41378     
41379     //private
41380     addicon : false,
41381     editicon: false,
41382     
41383     // element that contains real text value.. (when hidden is used..)
41384      
41385     // private
41386     onRender : function(ct, position)
41387     {
41388         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41389         
41390         if(this.hiddenName){
41391             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41392                     'before', true);
41393             this.hiddenField.value =
41394                 this.hiddenValue !== undefined ? this.hiddenValue :
41395                 this.value !== undefined ? this.value : '';
41396
41397             // prevent input submission
41398             this.el.dom.removeAttribute('name');
41399              
41400              
41401         }
41402         
41403         if(Roo.isGecko){
41404             this.el.dom.setAttribute('autocomplete', 'off');
41405         }
41406
41407         var cls = 'x-combo-list';
41408
41409         this.list = new Roo.Layer({
41410             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41411         });
41412
41413         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41414         this.list.setWidth(lw);
41415         this.list.swallowEvent('mousewheel');
41416         this.assetHeight = 0;
41417
41418         if(this.title){
41419             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41420             this.assetHeight += this.header.getHeight();
41421         }
41422
41423         this.innerList = this.list.createChild({cls:cls+'-inner'});
41424         this.innerList.on('mouseover', this.onViewOver, this);
41425         this.innerList.on('mousemove', this.onViewMove, this);
41426         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41427         
41428         if(this.allowBlank && !this.pageSize && !this.disableClear){
41429             this.footer = this.list.createChild({cls:cls+'-ft'});
41430             this.pageTb = new Roo.Toolbar(this.footer);
41431            
41432         }
41433         if(this.pageSize){
41434             this.footer = this.list.createChild({cls:cls+'-ft'});
41435             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41436                     {pageSize: this.pageSize});
41437             
41438         }
41439         
41440         if (this.pageTb && this.allowBlank && !this.disableClear) {
41441             var _this = this;
41442             this.pageTb.add(new Roo.Toolbar.Fill(), {
41443                 cls: 'x-btn-icon x-btn-clear',
41444                 text: '&#160;',
41445                 handler: function()
41446                 {
41447                     _this.collapse();
41448                     _this.clearValue();
41449                     _this.onSelect(false, -1);
41450                 }
41451             });
41452         }
41453         if (this.footer) {
41454             this.assetHeight += this.footer.getHeight();
41455         }
41456         
41457
41458         if(!this.tpl){
41459             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41460         }
41461
41462         this.view = new Roo.View(this.innerList, this.tpl, {
41463             singleSelect:true,
41464             store: this.store,
41465             selectedClass: this.selectedClass
41466         });
41467
41468         this.view.on('click', this.onViewClick, this);
41469
41470         this.store.on('beforeload', this.onBeforeLoad, this);
41471         this.store.on('load', this.onLoad, this);
41472         this.store.on('loadexception', this.onLoadException, this);
41473
41474         if(this.resizable){
41475             this.resizer = new Roo.Resizable(this.list,  {
41476                pinned:true, handles:'se'
41477             });
41478             this.resizer.on('resize', function(r, w, h){
41479                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41480                 this.listWidth = w;
41481                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41482                 this.restrictHeight();
41483             }, this);
41484             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41485         }
41486         if(!this.editable){
41487             this.editable = true;
41488             this.setEditable(false);
41489         }  
41490         
41491         
41492         if (typeof(this.events.add.listeners) != 'undefined') {
41493             
41494             this.addicon = this.wrap.createChild(
41495                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41496        
41497             this.addicon.on('click', function(e) {
41498                 this.fireEvent('add', this);
41499             }, this);
41500         }
41501         if (typeof(this.events.edit.listeners) != 'undefined') {
41502             
41503             this.editicon = this.wrap.createChild(
41504                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41505             if (this.addicon) {
41506                 this.editicon.setStyle('margin-left', '40px');
41507             }
41508             this.editicon.on('click', function(e) {
41509                 
41510                 // we fire even  if inothing is selected..
41511                 this.fireEvent('edit', this, this.lastData );
41512                 
41513             }, this);
41514         }
41515         
41516         
41517         
41518     },
41519
41520     // private
41521     initEvents : function(){
41522         Roo.form.ComboBox.superclass.initEvents.call(this);
41523
41524         this.keyNav = new Roo.KeyNav(this.el, {
41525             "up" : function(e){
41526                 this.inKeyMode = true;
41527                 this.selectPrev();
41528             },
41529
41530             "down" : function(e){
41531                 if(!this.isExpanded()){
41532                     this.onTriggerClick();
41533                 }else{
41534                     this.inKeyMode = true;
41535                     this.selectNext();
41536                 }
41537             },
41538
41539             "enter" : function(e){
41540                 this.onViewClick();
41541                 //return true;
41542             },
41543
41544             "esc" : function(e){
41545                 this.collapse();
41546             },
41547
41548             "tab" : function(e){
41549                 this.onViewClick(false);
41550                 this.fireEvent("specialkey", this, e);
41551                 return true;
41552             },
41553
41554             scope : this,
41555
41556             doRelay : function(foo, bar, hname){
41557                 if(hname == 'down' || this.scope.isExpanded()){
41558                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41559                 }
41560                 return true;
41561             },
41562
41563             forceKeyDown: true
41564         });
41565         this.queryDelay = Math.max(this.queryDelay || 10,
41566                 this.mode == 'local' ? 10 : 250);
41567         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41568         if(this.typeAhead){
41569             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41570         }
41571         if(this.editable !== false){
41572             this.el.on("keyup", this.onKeyUp, this);
41573         }
41574         if(this.forceSelection){
41575             this.on('blur', this.doForce, this);
41576         }
41577     },
41578
41579     onDestroy : function(){
41580         if(this.view){
41581             this.view.setStore(null);
41582             this.view.el.removeAllListeners();
41583             this.view.el.remove();
41584             this.view.purgeListeners();
41585         }
41586         if(this.list){
41587             this.list.destroy();
41588         }
41589         if(this.store){
41590             this.store.un('beforeload', this.onBeforeLoad, this);
41591             this.store.un('load', this.onLoad, this);
41592             this.store.un('loadexception', this.onLoadException, this);
41593         }
41594         Roo.form.ComboBox.superclass.onDestroy.call(this);
41595     },
41596
41597     // private
41598     fireKey : function(e){
41599         if(e.isNavKeyPress() && !this.list.isVisible()){
41600             this.fireEvent("specialkey", this, e);
41601         }
41602     },
41603
41604     // private
41605     onResize: function(w, h){
41606         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41607         
41608         if(typeof w != 'number'){
41609             // we do not handle it!?!?
41610             return;
41611         }
41612         var tw = this.trigger.getWidth();
41613         tw += this.addicon ? this.addicon.getWidth() : 0;
41614         tw += this.editicon ? this.editicon.getWidth() : 0;
41615         var x = w - tw;
41616         this.el.setWidth( this.adjustWidth('input', x));
41617             
41618         this.trigger.setStyle('left', x+'px');
41619         
41620         if(this.list && this.listWidth === undefined){
41621             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41622             this.list.setWidth(lw);
41623             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41624         }
41625         
41626     
41627         
41628     },
41629
41630     /**
41631      * Allow or prevent the user from directly editing the field text.  If false is passed,
41632      * the user will only be able to select from the items defined in the dropdown list.  This method
41633      * is the runtime equivalent of setting the 'editable' config option at config time.
41634      * @param {Boolean} value True to allow the user to directly edit the field text
41635      */
41636     setEditable : function(value){
41637         if(value == this.editable){
41638             return;
41639         }
41640         this.editable = value;
41641         if(!value){
41642             this.el.dom.setAttribute('readOnly', true);
41643             this.el.on('mousedown', this.onTriggerClick,  this);
41644             this.el.addClass('x-combo-noedit');
41645         }else{
41646             this.el.dom.setAttribute('readOnly', false);
41647             this.el.un('mousedown', this.onTriggerClick,  this);
41648             this.el.removeClass('x-combo-noedit');
41649         }
41650     },
41651
41652     // private
41653     onBeforeLoad : function(){
41654         if(!this.hasFocus){
41655             return;
41656         }
41657         this.innerList.update(this.loadingText ?
41658                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41659         this.restrictHeight();
41660         this.selectedIndex = -1;
41661     },
41662
41663     // private
41664     onLoad : function(){
41665         if(!this.hasFocus){
41666             return;
41667         }
41668         if(this.store.getCount() > 0){
41669             this.expand();
41670             this.restrictHeight();
41671             if(this.lastQuery == this.allQuery){
41672                 if(this.editable){
41673                     this.el.dom.select();
41674                 }
41675                 if(!this.selectByValue(this.value, true)){
41676                     this.select(0, true);
41677                 }
41678             }else{
41679                 this.selectNext();
41680                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41681                     this.taTask.delay(this.typeAheadDelay);
41682                 }
41683             }
41684         }else{
41685             this.onEmptyResults();
41686         }
41687         //this.el.focus();
41688     },
41689     // private
41690     onLoadException : function()
41691     {
41692         this.collapse();
41693         Roo.log(this.store.reader.jsonData);
41694         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41695             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41696         }
41697         
41698         
41699     },
41700     // private
41701     onTypeAhead : function(){
41702         if(this.store.getCount() > 0){
41703             var r = this.store.getAt(0);
41704             var newValue = r.data[this.displayField];
41705             var len = newValue.length;
41706             var selStart = this.getRawValue().length;
41707             if(selStart != len){
41708                 this.setRawValue(newValue);
41709                 this.selectText(selStart, newValue.length);
41710             }
41711         }
41712     },
41713
41714     // private
41715     onSelect : function(record, index){
41716         if(this.fireEvent('beforeselect', this, record, index) !== false){
41717             this.setFromData(index > -1 ? record.data : false);
41718             this.collapse();
41719             this.fireEvent('select', this, record, index);
41720         }
41721     },
41722
41723     /**
41724      * Returns the currently selected field value or empty string if no value is set.
41725      * @return {String} value The selected value
41726      */
41727     getValue : function(){
41728         if(this.valueField){
41729             return typeof this.value != 'undefined' ? this.value : '';
41730         }
41731         return Roo.form.ComboBox.superclass.getValue.call(this);
41732     },
41733
41734     /**
41735      * Clears any text/value currently set in the field
41736      */
41737     clearValue : function(){
41738         if(this.hiddenField){
41739             this.hiddenField.value = '';
41740         }
41741         this.value = '';
41742         this.setRawValue('');
41743         this.lastSelectionText = '';
41744         
41745     },
41746
41747     /**
41748      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41749      * will be displayed in the field.  If the value does not match the data value of an existing item,
41750      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41751      * Otherwise the field will be blank (although the value will still be set).
41752      * @param {String} value The value to match
41753      */
41754     setValue : function(v){
41755         var text = v;
41756         if(this.valueField){
41757             var r = this.findRecord(this.valueField, v);
41758             if(r){
41759                 text = r.data[this.displayField];
41760             }else if(this.valueNotFoundText !== undefined){
41761                 text = this.valueNotFoundText;
41762             }
41763         }
41764         this.lastSelectionText = text;
41765         if(this.hiddenField){
41766             this.hiddenField.value = v;
41767         }
41768         Roo.form.ComboBox.superclass.setValue.call(this, text);
41769         this.value = v;
41770     },
41771     /**
41772      * @property {Object} the last set data for the element
41773      */
41774     
41775     lastData : false,
41776     /**
41777      * Sets the value of the field based on a object which is related to the record format for the store.
41778      * @param {Object} value the value to set as. or false on reset?
41779      */
41780     setFromData : function(o){
41781         var dv = ''; // display value
41782         var vv = ''; // value value..
41783         this.lastData = o;
41784         if (this.displayField) {
41785             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41786         } else {
41787             // this is an error condition!!!
41788             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41789         }
41790         
41791         if(this.valueField){
41792             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41793         }
41794         if(this.hiddenField){
41795             this.hiddenField.value = vv;
41796             
41797             this.lastSelectionText = dv;
41798             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41799             this.value = vv;
41800             return;
41801         }
41802         // no hidden field.. - we store the value in 'value', but still display
41803         // display field!!!!
41804         this.lastSelectionText = dv;
41805         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41806         this.value = vv;
41807         
41808         
41809     },
41810     // private
41811     reset : function(){
41812         // overridden so that last data is reset..
41813         this.setValue(this.resetValue);
41814         this.originalValue = this.getValue();
41815         this.clearInvalid();
41816         this.lastData = false;
41817         if (this.view) {
41818             this.view.clearSelections();
41819         }
41820     },
41821     // private
41822     findRecord : function(prop, value){
41823         var record;
41824         if(this.store.getCount() > 0){
41825             this.store.each(function(r){
41826                 if(r.data[prop] == value){
41827                     record = r;
41828                     return false;
41829                 }
41830                 return true;
41831             });
41832         }
41833         return record;
41834     },
41835     
41836     getName: function()
41837     {
41838         // returns hidden if it's set..
41839         if (!this.rendered) {return ''};
41840         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41841         
41842     },
41843     // private
41844     onViewMove : function(e, t){
41845         this.inKeyMode = false;
41846     },
41847
41848     // private
41849     onViewOver : function(e, t){
41850         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41851             return;
41852         }
41853         var item = this.view.findItemFromChild(t);
41854         if(item){
41855             var index = this.view.indexOf(item);
41856             this.select(index, false);
41857         }
41858     },
41859
41860     // private
41861     onViewClick : function(doFocus)
41862     {
41863         var index = this.view.getSelectedIndexes()[0];
41864         var r = this.store.getAt(index);
41865         if(r){
41866             this.onSelect(r, index);
41867         }
41868         if(doFocus !== false && !this.blockFocus){
41869             this.el.focus();
41870         }
41871     },
41872
41873     // private
41874     restrictHeight : function(){
41875         this.innerList.dom.style.height = '';
41876         var inner = this.innerList.dom;
41877         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41878         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41879         this.list.beginUpdate();
41880         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41881         this.list.alignTo(this.el, this.listAlign);
41882         this.list.endUpdate();
41883     },
41884
41885     // private
41886     onEmptyResults : function(){
41887         this.collapse();
41888     },
41889
41890     /**
41891      * Returns true if the dropdown list is expanded, else false.
41892      */
41893     isExpanded : function(){
41894         return this.list.isVisible();
41895     },
41896
41897     /**
41898      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41899      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41900      * @param {String} value The data value of the item to select
41901      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41902      * selected item if it is not currently in view (defaults to true)
41903      * @return {Boolean} True if the value matched an item in the list, else false
41904      */
41905     selectByValue : function(v, scrollIntoView){
41906         if(v !== undefined && v !== null){
41907             var r = this.findRecord(this.valueField || this.displayField, v);
41908             if(r){
41909                 this.select(this.store.indexOf(r), scrollIntoView);
41910                 return true;
41911             }
41912         }
41913         return false;
41914     },
41915
41916     /**
41917      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41918      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41919      * @param {Number} index The zero-based index of the list item to select
41920      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41921      * selected item if it is not currently in view (defaults to true)
41922      */
41923     select : function(index, scrollIntoView){
41924         this.selectedIndex = index;
41925         this.view.select(index);
41926         if(scrollIntoView !== false){
41927             var el = this.view.getNode(index);
41928             if(el){
41929                 this.innerList.scrollChildIntoView(el, false);
41930             }
41931         }
41932     },
41933
41934     // private
41935     selectNext : function(){
41936         var ct = this.store.getCount();
41937         if(ct > 0){
41938             if(this.selectedIndex == -1){
41939                 this.select(0);
41940             }else if(this.selectedIndex < ct-1){
41941                 this.select(this.selectedIndex+1);
41942             }
41943         }
41944     },
41945
41946     // private
41947     selectPrev : function(){
41948         var ct = this.store.getCount();
41949         if(ct > 0){
41950             if(this.selectedIndex == -1){
41951                 this.select(0);
41952             }else if(this.selectedIndex != 0){
41953                 this.select(this.selectedIndex-1);
41954             }
41955         }
41956     },
41957
41958     // private
41959     onKeyUp : function(e){
41960         if(this.editable !== false && !e.isSpecialKey()){
41961             this.lastKey = e.getKey();
41962             this.dqTask.delay(this.queryDelay);
41963         }
41964     },
41965
41966     // private
41967     validateBlur : function(){
41968         return !this.list || !this.list.isVisible();   
41969     },
41970
41971     // private
41972     initQuery : function(){
41973         this.doQuery(this.getRawValue());
41974     },
41975
41976     // private
41977     doForce : function(){
41978         if(this.el.dom.value.length > 0){
41979             this.el.dom.value =
41980                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41981              
41982         }
41983     },
41984
41985     /**
41986      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41987      * query allowing the query action to be canceled if needed.
41988      * @param {String} query The SQL query to execute
41989      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41990      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41991      * saved in the current store (defaults to false)
41992      */
41993     doQuery : function(q, forceAll){
41994         if(q === undefined || q === null){
41995             q = '';
41996         }
41997         var qe = {
41998             query: q,
41999             forceAll: forceAll,
42000             combo: this,
42001             cancel:false
42002         };
42003         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42004             return false;
42005         }
42006         q = qe.query;
42007         forceAll = qe.forceAll;
42008         if(forceAll === true || (q.length >= this.minChars)){
42009             if(this.lastQuery != q || this.alwaysQuery){
42010                 this.lastQuery = q;
42011                 if(this.mode == 'local'){
42012                     this.selectedIndex = -1;
42013                     if(forceAll){
42014                         this.store.clearFilter();
42015                     }else{
42016                         this.store.filter(this.displayField, q);
42017                     }
42018                     this.onLoad();
42019                 }else{
42020                     this.store.baseParams[this.queryParam] = q;
42021                     this.store.load({
42022                         params: this.getParams(q)
42023                     });
42024                     this.expand();
42025                 }
42026             }else{
42027                 this.selectedIndex = -1;
42028                 this.onLoad();   
42029             }
42030         }
42031     },
42032
42033     // private
42034     getParams : function(q){
42035         var p = {};
42036         //p[this.queryParam] = q;
42037         if(this.pageSize){
42038             p.start = 0;
42039             p.limit = this.pageSize;
42040         }
42041         return p;
42042     },
42043
42044     /**
42045      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42046      */
42047     collapse : function(){
42048         if(!this.isExpanded()){
42049             return;
42050         }
42051         this.list.hide();
42052         Roo.get(document).un('mousedown', this.collapseIf, this);
42053         Roo.get(document).un('mousewheel', this.collapseIf, this);
42054         if (!this.editable) {
42055             Roo.get(document).un('keydown', this.listKeyPress, this);
42056         }
42057         this.fireEvent('collapse', this);
42058     },
42059
42060     // private
42061     collapseIf : function(e){
42062         if(!e.within(this.wrap) && !e.within(this.list)){
42063             this.collapse();
42064         }
42065     },
42066
42067     /**
42068      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42069      */
42070     expand : function(){
42071         if(this.isExpanded() || !this.hasFocus){
42072             return;
42073         }
42074         this.list.alignTo(this.el, this.listAlign);
42075         this.list.show();
42076         Roo.get(document).on('mousedown', this.collapseIf, this);
42077         Roo.get(document).on('mousewheel', this.collapseIf, this);
42078         if (!this.editable) {
42079             Roo.get(document).on('keydown', this.listKeyPress, this);
42080         }
42081         
42082         this.fireEvent('expand', this);
42083     },
42084
42085     // private
42086     // Implements the default empty TriggerField.onTriggerClick function
42087     onTriggerClick : function(){
42088         if(this.disabled){
42089             return;
42090         }
42091         if(this.isExpanded()){
42092             this.collapse();
42093             if (!this.blockFocus) {
42094                 this.el.focus();
42095             }
42096             
42097         }else {
42098             this.hasFocus = true;
42099             if(this.triggerAction == 'all') {
42100                 this.doQuery(this.allQuery, true);
42101             } else {
42102                 this.doQuery(this.getRawValue());
42103             }
42104             if (!this.blockFocus) {
42105                 this.el.focus();
42106             }
42107         }
42108     },
42109     listKeyPress : function(e)
42110     {
42111         //Roo.log('listkeypress');
42112         // scroll to first matching element based on key pres..
42113         if (e.isSpecialKey()) {
42114             return false;
42115         }
42116         var k = String.fromCharCode(e.getKey()).toUpperCase();
42117         //Roo.log(k);
42118         var match  = false;
42119         var csel = this.view.getSelectedNodes();
42120         var cselitem = false;
42121         if (csel.length) {
42122             var ix = this.view.indexOf(csel[0]);
42123             cselitem  = this.store.getAt(ix);
42124             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42125                 cselitem = false;
42126             }
42127             
42128         }
42129         
42130         this.store.each(function(v) { 
42131             if (cselitem) {
42132                 // start at existing selection.
42133                 if (cselitem.id == v.id) {
42134                     cselitem = false;
42135                 }
42136                 return;
42137             }
42138                 
42139             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42140                 match = this.store.indexOf(v);
42141                 return false;
42142             }
42143         }, this);
42144         
42145         if (match === false) {
42146             return true; // no more action?
42147         }
42148         // scroll to?
42149         this.view.select(match);
42150         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42151         sn.scrollIntoView(sn.dom.parentNode, false);
42152     } 
42153
42154     /** 
42155     * @cfg {Boolean} grow 
42156     * @hide 
42157     */
42158     /** 
42159     * @cfg {Number} growMin 
42160     * @hide 
42161     */
42162     /** 
42163     * @cfg {Number} growMax 
42164     * @hide 
42165     */
42166     /**
42167      * @hide
42168      * @method autoSize
42169      */
42170 });/*
42171  * Copyright(c) 2010-2012, Roo J Solutions Limited
42172  *
42173  * Licence LGPL
42174  *
42175  */
42176
42177 /**
42178  * @class Roo.form.ComboBoxArray
42179  * @extends Roo.form.TextField
42180  * A facebook style adder... for lists of email / people / countries  etc...
42181  * pick multiple items from a combo box, and shows each one.
42182  *
42183  *  Fred [x]  Brian [x]  [Pick another |v]
42184  *
42185  *
42186  *  For this to work: it needs various extra information
42187  *    - normal combo problay has
42188  *      name, hiddenName
42189  *    + displayField, valueField
42190  *
42191  *    For our purpose...
42192  *
42193  *
42194  *   If we change from 'extends' to wrapping...
42195  *   
42196  *  
42197  *
42198  
42199  
42200  * @constructor
42201  * Create a new ComboBoxArray.
42202  * @param {Object} config Configuration options
42203  */
42204  
42205
42206 Roo.form.ComboBoxArray = function(config)
42207 {
42208     this.addEvents({
42209         /**
42210          * @event beforeremove
42211          * Fires before remove the value from the list
42212              * @param {Roo.form.ComboBoxArray} _self This combo box array
42213              * @param {Roo.form.ComboBoxArray.Item} item removed item
42214              */
42215         'beforeremove' : true,
42216         /**
42217          * @event remove
42218          * Fires when remove the value from the list
42219              * @param {Roo.form.ComboBoxArray} _self This combo box array
42220              * @param {Roo.form.ComboBoxArray.Item} item removed item
42221              */
42222         'remove' : true
42223         
42224         
42225     });
42226     
42227     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42228     
42229     this.items = new Roo.util.MixedCollection(false);
42230     
42231     // construct the child combo...
42232     
42233     
42234     
42235     
42236    
42237     
42238 }
42239
42240  
42241 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42242
42243     /**
42244      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42245      */
42246     
42247     lastData : false,
42248     
42249     // behavies liek a hiddne field
42250     inputType:      'hidden',
42251     /**
42252      * @cfg {Number} width The width of the box that displays the selected element
42253      */ 
42254     width:          300,
42255
42256     
42257     
42258     /**
42259      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42260      */
42261     name : false,
42262     /**
42263      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42264      */
42265     hiddenName : false,
42266     
42267     
42268     // private the array of items that are displayed..
42269     items  : false,
42270     // private - the hidden field el.
42271     hiddenEl : false,
42272     // private - the filed el..
42273     el : false,
42274     
42275     //validateValue : function() { return true; }, // all values are ok!
42276     //onAddClick: function() { },
42277     
42278     onRender : function(ct, position) 
42279     {
42280         
42281         // create the standard hidden element
42282         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42283         
42284         
42285         // give fake names to child combo;
42286         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42287         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42288         
42289         this.combo = Roo.factory(this.combo, Roo.form);
42290         this.combo.onRender(ct, position);
42291         if (typeof(this.combo.width) != 'undefined') {
42292             this.combo.onResize(this.combo.width,0);
42293         }
42294         
42295         this.combo.initEvents();
42296         
42297         // assigned so form know we need to do this..
42298         this.store          = this.combo.store;
42299         this.valueField     = this.combo.valueField;
42300         this.displayField   = this.combo.displayField ;
42301         
42302         
42303         this.combo.wrap.addClass('x-cbarray-grp');
42304         
42305         var cbwrap = this.combo.wrap.createChild(
42306             {tag: 'div', cls: 'x-cbarray-cb'},
42307             this.combo.el.dom
42308         );
42309         
42310              
42311         this.hiddenEl = this.combo.wrap.createChild({
42312             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42313         });
42314         this.el = this.combo.wrap.createChild({
42315             tag: 'input',  type:'hidden' , name: this.name, value : ''
42316         });
42317          //   this.el.dom.removeAttribute("name");
42318         
42319         
42320         this.outerWrap = this.combo.wrap;
42321         this.wrap = cbwrap;
42322         
42323         this.outerWrap.setWidth(this.width);
42324         this.outerWrap.dom.removeChild(this.el.dom);
42325         
42326         this.wrap.dom.appendChild(this.el.dom);
42327         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42328         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42329         
42330         this.combo.trigger.setStyle('position','relative');
42331         this.combo.trigger.setStyle('left', '0px');
42332         this.combo.trigger.setStyle('top', '2px');
42333         
42334         this.combo.el.setStyle('vertical-align', 'text-bottom');
42335         
42336         //this.trigger.setStyle('vertical-align', 'top');
42337         
42338         // this should use the code from combo really... on('add' ....)
42339         if (this.adder) {
42340             
42341         
42342             this.adder = this.outerWrap.createChild(
42343                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42344             var _t = this;
42345             this.adder.on('click', function(e) {
42346                 _t.fireEvent('adderclick', this, e);
42347             }, _t);
42348         }
42349         //var _t = this;
42350         //this.adder.on('click', this.onAddClick, _t);
42351         
42352         
42353         this.combo.on('select', function(cb, rec, ix) {
42354             this.addItem(rec.data);
42355             
42356             cb.setValue('');
42357             cb.el.dom.value = '';
42358             //cb.lastData = rec.data;
42359             // add to list
42360             
42361         }, this);
42362         
42363         
42364     },
42365     
42366     
42367     getName: function()
42368     {
42369         // returns hidden if it's set..
42370         if (!this.rendered) {return ''};
42371         return  this.hiddenName ? this.hiddenName : this.name;
42372         
42373     },
42374     
42375     
42376     onResize: function(w, h){
42377         
42378         return;
42379         // not sure if this is needed..
42380         //this.combo.onResize(w,h);
42381         
42382         if(typeof w != 'number'){
42383             // we do not handle it!?!?
42384             return;
42385         }
42386         var tw = this.combo.trigger.getWidth();
42387         tw += this.addicon ? this.addicon.getWidth() : 0;
42388         tw += this.editicon ? this.editicon.getWidth() : 0;
42389         var x = w - tw;
42390         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42391             
42392         this.combo.trigger.setStyle('left', '0px');
42393         
42394         if(this.list && this.listWidth === undefined){
42395             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42396             this.list.setWidth(lw);
42397             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42398         }
42399         
42400     
42401         
42402     },
42403     
42404     addItem: function(rec)
42405     {
42406         var valueField = this.combo.valueField;
42407         var displayField = this.combo.displayField;
42408         
42409         if (this.items.indexOfKey(rec[valueField]) > -1) {
42410             //console.log("GOT " + rec.data.id);
42411             return;
42412         }
42413         
42414         var x = new Roo.form.ComboBoxArray.Item({
42415             //id : rec[this.idField],
42416             data : rec,
42417             displayField : displayField ,
42418             tipField : displayField ,
42419             cb : this
42420         });
42421         // use the 
42422         this.items.add(rec[valueField],x);
42423         // add it before the element..
42424         this.updateHiddenEl();
42425         x.render(this.outerWrap, this.wrap.dom);
42426         // add the image handler..
42427     },
42428     
42429     updateHiddenEl : function()
42430     {
42431         this.validate();
42432         if (!this.hiddenEl) {
42433             return;
42434         }
42435         var ar = [];
42436         var idField = this.combo.valueField;
42437         
42438         this.items.each(function(f) {
42439             ar.push(f.data[idField]);
42440         });
42441         this.hiddenEl.dom.value = ar.join(',');
42442         this.validate();
42443     },
42444     
42445     reset : function()
42446     {
42447         this.items.clear();
42448         
42449         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42450            el.remove();
42451         });
42452         
42453         this.el.dom.value = '';
42454         if (this.hiddenEl) {
42455             this.hiddenEl.dom.value = '';
42456         }
42457         
42458     },
42459     getValue: function()
42460     {
42461         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42462     },
42463     setValue: function(v) // not a valid action - must use addItems..
42464     {
42465         
42466         this.reset();
42467          
42468         if (this.store.isLocal && (typeof(v) == 'string')) {
42469             // then we can use the store to find the values..
42470             // comma seperated at present.. this needs to allow JSON based encoding..
42471             this.hiddenEl.value  = v;
42472             var v_ar = [];
42473             Roo.each(v.split(','), function(k) {
42474                 Roo.log("CHECK " + this.valueField + ',' + k);
42475                 var li = this.store.query(this.valueField, k);
42476                 if (!li.length) {
42477                     return;
42478                 }
42479                 var add = {};
42480                 add[this.valueField] = k;
42481                 add[this.displayField] = li.item(0).data[this.displayField];
42482                 
42483                 this.addItem(add);
42484             }, this) 
42485              
42486         }
42487         if (typeof(v) == 'object' ) {
42488             // then let's assume it's an array of objects..
42489             Roo.each(v, function(l) {
42490                 this.addItem(l);
42491             }, this);
42492              
42493         }
42494         
42495         
42496     },
42497     setFromData: function(v)
42498     {
42499         // this recieves an object, if setValues is called.
42500         this.reset();
42501         this.el.dom.value = v[this.displayField];
42502         this.hiddenEl.dom.value = v[this.valueField];
42503         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42504             return;
42505         }
42506         var kv = v[this.valueField];
42507         var dv = v[this.displayField];
42508         kv = typeof(kv) != 'string' ? '' : kv;
42509         dv = typeof(dv) != 'string' ? '' : dv;
42510         
42511         
42512         var keys = kv.split(',');
42513         var display = dv.split(',');
42514         for (var i = 0 ; i < keys.length; i++) {
42515             
42516             add = {};
42517             add[this.valueField] = keys[i];
42518             add[this.displayField] = display[i];
42519             this.addItem(add);
42520         }
42521       
42522         
42523     },
42524     
42525     /**
42526      * Validates the combox array value
42527      * @return {Boolean} True if the value is valid, else false
42528      */
42529     validate : function(){
42530         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42531             this.clearInvalid();
42532             return true;
42533         }
42534         return false;
42535     },
42536     
42537     validateValue : function(value){
42538         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42539         
42540     },
42541     
42542     /*@
42543      * overide
42544      * 
42545      */
42546     isDirty : function() {
42547         if(this.disabled) {
42548             return false;
42549         }
42550         
42551         try {
42552             var d = Roo.decode(String(this.originalValue));
42553         } catch (e) {
42554             return String(this.getValue()) !== String(this.originalValue);
42555         }
42556         
42557         var originalValue = [];
42558         
42559         for (var i = 0; i < d.length; i++){
42560             originalValue.push(d[i][this.valueField]);
42561         }
42562         
42563         return String(this.getValue()) !== String(originalValue.join(','));
42564         
42565     }
42566     
42567 });
42568
42569
42570
42571 /**
42572  * @class Roo.form.ComboBoxArray.Item
42573  * @extends Roo.BoxComponent
42574  * A selected item in the list
42575  *  Fred [x]  Brian [x]  [Pick another |v]
42576  * 
42577  * @constructor
42578  * Create a new item.
42579  * @param {Object} config Configuration options
42580  */
42581  
42582 Roo.form.ComboBoxArray.Item = function(config) {
42583     config.id = Roo.id();
42584     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42585 }
42586
42587 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42588     data : {},
42589     cb: false,
42590     displayField : false,
42591     tipField : false,
42592     
42593     
42594     defaultAutoCreate : {
42595         tag: 'div',
42596         cls: 'x-cbarray-item',
42597         cn : [ 
42598             { tag: 'div' },
42599             {
42600                 tag: 'img',
42601                 width:16,
42602                 height : 16,
42603                 src : Roo.BLANK_IMAGE_URL ,
42604                 align: 'center'
42605             }
42606         ]
42607         
42608     },
42609     
42610  
42611     onRender : function(ct, position)
42612     {
42613         Roo.form.Field.superclass.onRender.call(this, ct, position);
42614         
42615         if(!this.el){
42616             var cfg = this.getAutoCreate();
42617             this.el = ct.createChild(cfg, position);
42618         }
42619         
42620         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42621         
42622         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42623             this.cb.renderer(this.data) :
42624             String.format('{0}',this.data[this.displayField]);
42625         
42626             
42627         this.el.child('div').dom.setAttribute('qtip',
42628                         String.format('{0}',this.data[this.tipField])
42629         );
42630         
42631         this.el.child('img').on('click', this.remove, this);
42632         
42633     },
42634    
42635     remove : function()
42636     {
42637         if(this.cb.disabled){
42638             return;
42639         }
42640         
42641         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42642             this.cb.items.remove(this);
42643             this.el.child('img').un('click', this.remove, this);
42644             this.el.remove();
42645             this.cb.updateHiddenEl();
42646
42647             this.cb.fireEvent('remove', this.cb, this);
42648         }
42649         
42650     }
42651 });/*
42652  * RooJS Library 1.1.1
42653  * Copyright(c) 2008-2011  Alan Knowles
42654  *
42655  * License - LGPL
42656  */
42657  
42658
42659 /**
42660  * @class Roo.form.ComboNested
42661  * @extends Roo.form.ComboBox
42662  * A combobox for that allows selection of nested items in a list,
42663  * eg.
42664  *
42665  *  Book
42666  *    -> red
42667  *    -> green
42668  *  Table
42669  *    -> square
42670  *      ->red
42671  *      ->green
42672  *    -> rectangle
42673  *      ->green
42674  *      
42675  * 
42676  * @constructor
42677  * Create a new ComboNested
42678  * @param {Object} config Configuration options
42679  */
42680 Roo.form.ComboNested = function(config){
42681     Roo.form.ComboCheck.superclass.constructor.call(this, config);
42682     // should verify some data...
42683     // like
42684     // hiddenName = required..
42685     // displayField = required
42686     // valudField == required
42687     var req= [ 'hiddenName', 'displayField', 'valueField' ];
42688     var _t = this;
42689     Roo.each(req, function(e) {
42690         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
42691             throw "Roo.form.ComboNested : missing value for: " + e;
42692         }
42693     });
42694      
42695     
42696 };
42697
42698 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
42699    
42700    
42701     list : null, // the outermost div..
42702     innerLists : null, // the
42703     views : null,
42704     stores : null,
42705     // private
42706     onRender : function(ct, position)
42707     {
42708         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
42709         
42710         if(this.hiddenName){
42711             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42712                     'before', true);
42713             this.hiddenField.value =
42714                 this.hiddenValue !== undefined ? this.hiddenValue :
42715                 this.value !== undefined ? this.value : '';
42716
42717             // prevent input submission
42718             this.el.dom.removeAttribute('name');
42719              
42720              
42721         }
42722         
42723         if(Roo.isGecko){
42724             this.el.dom.setAttribute('autocomplete', 'off');
42725         }
42726
42727         var cls = 'x-combo-list';
42728
42729         this.list = new Roo.Layer({
42730             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42731         });
42732
42733         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42734         this.list.setWidth(lw);
42735         this.list.swallowEvent('mousewheel');
42736         this.assetHeight = 0;
42737
42738         if(this.title){
42739             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42740             this.assetHeight += this.header.getHeight();
42741         }
42742         this.innerLists = [];
42743         this.views = [];
42744         this.stores = [];
42745         for (var i =0 ; i < 3; i++) {
42746             this.onRenderList( cls, i);
42747         }
42748         
42749         // always needs footer, as we are going to have an 'OK' button.
42750         this.footer = this.list.createChild({cls:cls+'-ft'});
42751         this.pageTb = new Roo.Toolbar(this.footer);  
42752         var _this = this;
42753         this.pageTb.add(  {
42754             
42755             text: 'Done',
42756             handler: function()
42757             {
42758                 _this.collapse();
42759             }
42760         });
42761         
42762         if ( this.allowBlank && !this.disableClear) {
42763             
42764             this.pageTb.add(new Roo.Toolbar.Fill(), {
42765                 cls: 'x-btn-icon x-btn-clear',
42766                 text: '&#160;',
42767                 handler: function()
42768                 {
42769                     _this.collapse();
42770                     _this.clearValue();
42771                     _this.onSelect(false, -1);
42772                 }
42773             });
42774         }
42775         if (this.footer) {
42776             this.assetHeight += this.footer.getHeight();
42777         }
42778         
42779     },
42780     onRenderList : function (  cls, i)
42781     {
42782         
42783         var lw = Math.floor(
42784                 ((this.listWidth * 3 || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / 3
42785         );
42786         
42787         this.list.setWidth(lw); // default to '1'
42788
42789         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
42790         //il.on('mouseover', this.onViewOver, this, { list:  i });
42791         //il.on('mousemove', this.onViewMove, this, { list:  i });
42792         il.setWidth(lw);
42793         il.setStyle({ 'overflow-x' : 'hidden'});
42794
42795         if(!this.tpl){
42796             this.tpl = new Roo.Template({
42797                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
42798                 isEmpty: function (value, allValues) {
42799                     return value.length ? 'has-children' : 'no-children'
42800                 }
42801             });
42802         }
42803         
42804         var store  = this.store;
42805         if (i > 0) {
42806             store  = new Roo.data.SimpleStore({
42807                 reader : this.store.reader,
42808                 data : [ ]
42809             });
42810         }
42811         this.stores[i]  = store;
42812                 
42813         
42814         
42815         var view = this.views[i] = new Roo.View(
42816             il,
42817             this.tpl,
42818             {
42819                 singleSelect:true,
42820                 store: store,
42821                 selectedClass: this.selectedClass
42822             }
42823         );
42824         view.getEl().setWidth(lw);
42825         view.getEl().setStyle({
42826             position: i < 1 ? 'relative' : 'absolute',
42827             top: 0,
42828             left: (i * lw ) + 'px',
42829             display : i > 0 ? 'none' : 'block'
42830         });
42831         view.on('selectionchange', this.onSelectChange, this, {list : i });
42832         view.on('dblclick', this.onDoubleClick, this, {list : i });
42833         //view.on('click', this.onViewClick, this, { list : i });
42834
42835         store.on('beforeload', this.onBeforeLoad, this);
42836         store.on('load',  this.onStoreLoad, this, { list  : i});
42837         store.on('loadexception', this.onLoadException, this);
42838
42839         // hide the other vies..
42840         
42841         
42842         
42843     },
42844     onResize : function()  {},
42845     
42846     restrictHeight : function()
42847     {
42848         var mh = 0;
42849         Roo.each(this.innerLists, function(il,i) {
42850             var el = this.views[i].getEl();
42851             el.dom.style.height = '';
42852             var inner = el.dom;
42853             var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42854             // only adjust heights on other ones..
42855             if (i < 1) {
42856                 
42857                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42858                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42859                 mh = Math.max(el.getHeight(), mh);
42860             }
42861             
42862             
42863         }, this);
42864         
42865         this.list.beginUpdate();
42866         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
42867         this.list.alignTo(this.el, this.listAlign);
42868         this.list.endUpdate();
42869         
42870     },
42871      
42872     
42873     // -- store handlers..
42874     
42875     // private
42876     onLoad : function(a,b,c,d)
42877     {
42878         
42879         if(!this.hasFocus){
42880             return;
42881         }
42882         
42883         if(this.store.getCount() > 0) {
42884             this.expand();
42885             this.restrictHeight();   
42886         } else {
42887             this.onEmptyResults();
42888         }
42889         /*
42890         this.stores[1].loadData([]);
42891         this.stores[2].loadData([]);
42892         this.views
42893         */    
42894     
42895         //this.el.focus();
42896     },
42897     onStoreLoad : function ()
42898     {
42899         Roo.log(arguments);
42900     },
42901     
42902     // private
42903     onLoadException : function()
42904     {
42905         this.collapse();
42906         Roo.log(this.store.reader.jsonData);
42907         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42908             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42909         }
42910         
42911         
42912     } ,
42913      
42914      
42915
42916     onSelectChange : function (view, sels, opts )
42917     {
42918         var ix = view.getSelectedIndexes();
42919         
42920         
42921         if (opts.list > 1) {
42922              
42923             this.setFromData(ix.length ? view.store.getAt(ix[0]).data : {});
42924             return;
42925         }
42926         
42927         if (!ix.length) {
42928             this.setFromData({});
42929             this.stores[opts.list+1].loadData( [] );
42930             return;
42931         }
42932         
42933         var rec = view.store.getAt(ix[0]);
42934         this.setFromData(rec.data);
42935         
42936         var lw = Math.floor(
42937                 ((this.listWidth * 3 || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / 3
42938         );
42939         
42940         this.stores[opts.list+1].loadData( typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn);
42941         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
42942         this.views[opts.list+1].getEl().setStyle({ display : rec.data.cn.length ? 'block' : 'none' });
42943         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
42944         this.list.setWidth(lw * (opts.list + (rec.data.cn.length ? 2 : 1))); 
42945     },
42946     onDoubleClick : function()
42947     {
42948         this.collapse(); //??
42949     },
42950     
42951      
42952     
42953     findRecord : function (prop,value)
42954     {
42955         return this.findRecordInStore(this.store, prop,value);
42956     },
42957     
42958      // private
42959     findRecordInStore : function(store, prop, value)
42960     {
42961         var cstore = new Roo.data.SimpleStore({
42962             reader : this.store.reader,
42963             data : [ ]
42964         });
42965         var _this = this;
42966         var record  = false;
42967         if(store.getCount() > 0){
42968            store.each(function(r){
42969                 if(r.data[prop] == value){
42970                     record = r;
42971                     return false;
42972                 }
42973                 if (r.data.cn && r.data.cn.length) {
42974                     cstore.loadData( r.data.cn);
42975                     var cret = _this.findRecordInStore(cstore, prop, value);
42976                     if (cret !== false) {
42977                         record = cret;
42978                         return false;
42979                     }
42980                 }
42981                 
42982                 return true;
42983             });
42984         }
42985         return record;
42986     }
42987     
42988 });/*
42989  * Based on:
42990  * Ext JS Library 1.1.1
42991  * Copyright(c) 2006-2007, Ext JS, LLC.
42992  *
42993  * Originally Released Under LGPL - original licence link has changed is not relivant.
42994  *
42995  * Fork - LGPL
42996  * <script type="text/javascript">
42997  */
42998 /**
42999  * @class Roo.form.Checkbox
43000  * @extends Roo.form.Field
43001  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43002  * @constructor
43003  * Creates a new Checkbox
43004  * @param {Object} config Configuration options
43005  */
43006 Roo.form.Checkbox = function(config){
43007     Roo.form.Checkbox.superclass.constructor.call(this, config);
43008     this.addEvents({
43009         /**
43010          * @event check
43011          * Fires when the checkbox is checked or unchecked.
43012              * @param {Roo.form.Checkbox} this This checkbox
43013              * @param {Boolean} checked The new checked value
43014              */
43015         check : true
43016     });
43017 };
43018
43019 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43020     /**
43021      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43022      */
43023     focusClass : undefined,
43024     /**
43025      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43026      */
43027     fieldClass: "x-form-field",
43028     /**
43029      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43030      */
43031     checked: false,
43032     /**
43033      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43034      * {tag: "input", type: "checkbox", autocomplete: "off"})
43035      */
43036     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43037     /**
43038      * @cfg {String} boxLabel The text that appears beside the checkbox
43039      */
43040     boxLabel : "",
43041     /**
43042      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43043      */  
43044     inputValue : '1',
43045     /**
43046      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43047      */
43048      valueOff: '0', // value when not checked..
43049
43050     actionMode : 'viewEl', 
43051     //
43052     // private
43053     itemCls : 'x-menu-check-item x-form-item',
43054     groupClass : 'x-menu-group-item',
43055     inputType : 'hidden',
43056     
43057     
43058     inSetChecked: false, // check that we are not calling self...
43059     
43060     inputElement: false, // real input element?
43061     basedOn: false, // ????
43062     
43063     isFormField: true, // not sure where this is needed!!!!
43064
43065     onResize : function(){
43066         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43067         if(!this.boxLabel){
43068             this.el.alignTo(this.wrap, 'c-c');
43069         }
43070     },
43071
43072     initEvents : function(){
43073         Roo.form.Checkbox.superclass.initEvents.call(this);
43074         this.el.on("click", this.onClick,  this);
43075         this.el.on("change", this.onClick,  this);
43076     },
43077
43078
43079     getResizeEl : function(){
43080         return this.wrap;
43081     },
43082
43083     getPositionEl : function(){
43084         return this.wrap;
43085     },
43086
43087     // private
43088     onRender : function(ct, position){
43089         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43090         /*
43091         if(this.inputValue !== undefined){
43092             this.el.dom.value = this.inputValue;
43093         }
43094         */
43095         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43096         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43097         var viewEl = this.wrap.createChild({ 
43098             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43099         this.viewEl = viewEl;   
43100         this.wrap.on('click', this.onClick,  this); 
43101         
43102         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43103         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43104         
43105         
43106         
43107         if(this.boxLabel){
43108             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43109         //    viewEl.on('click', this.onClick,  this); 
43110         }
43111         //if(this.checked){
43112             this.setChecked(this.checked);
43113         //}else{
43114             //this.checked = this.el.dom;
43115         //}
43116
43117     },
43118
43119     // private
43120     initValue : Roo.emptyFn,
43121
43122     /**
43123      * Returns the checked state of the checkbox.
43124      * @return {Boolean} True if checked, else false
43125      */
43126     getValue : function(){
43127         if(this.el){
43128             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43129         }
43130         return this.valueOff;
43131         
43132     },
43133
43134         // private
43135     onClick : function(){ 
43136         if (this.disabled) {
43137             return;
43138         }
43139         this.setChecked(!this.checked);
43140
43141         //if(this.el.dom.checked != this.checked){
43142         //    this.setValue(this.el.dom.checked);
43143        // }
43144     },
43145
43146     /**
43147      * Sets the checked state of the checkbox.
43148      * On is always based on a string comparison between inputValue and the param.
43149      * @param {Boolean/String} value - the value to set 
43150      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43151      */
43152     setValue : function(v,suppressEvent){
43153         
43154         
43155         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43156         //if(this.el && this.el.dom){
43157         //    this.el.dom.checked = this.checked;
43158         //    this.el.dom.defaultChecked = this.checked;
43159         //}
43160         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43161         //this.fireEvent("check", this, this.checked);
43162     },
43163     // private..
43164     setChecked : function(state,suppressEvent)
43165     {
43166         if (this.inSetChecked) {
43167             this.checked = state;
43168             return;
43169         }
43170         
43171     
43172         if(this.wrap){
43173             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43174         }
43175         this.checked = state;
43176         if(suppressEvent !== true){
43177             this.fireEvent('check', this, state);
43178         }
43179         this.inSetChecked = true;
43180         this.el.dom.value = state ? this.inputValue : this.valueOff;
43181         this.inSetChecked = false;
43182         
43183     },
43184     // handle setting of hidden value by some other method!!?!?
43185     setFromHidden: function()
43186     {
43187         if(!this.el){
43188             return;
43189         }
43190         //console.log("SET FROM HIDDEN");
43191         //alert('setFrom hidden');
43192         this.setValue(this.el.dom.value);
43193     },
43194     
43195     onDestroy : function()
43196     {
43197         if(this.viewEl){
43198             Roo.get(this.viewEl).remove();
43199         }
43200          
43201         Roo.form.Checkbox.superclass.onDestroy.call(this);
43202     },
43203     
43204     setBoxLabel : function(str)
43205     {
43206         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43207     }
43208
43209 });/*
43210  * Based on:
43211  * Ext JS Library 1.1.1
43212  * Copyright(c) 2006-2007, Ext JS, LLC.
43213  *
43214  * Originally Released Under LGPL - original licence link has changed is not relivant.
43215  *
43216  * Fork - LGPL
43217  * <script type="text/javascript">
43218  */
43219  
43220 /**
43221  * @class Roo.form.Radio
43222  * @extends Roo.form.Checkbox
43223  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43224  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43225  * @constructor
43226  * Creates a new Radio
43227  * @param {Object} config Configuration options
43228  */
43229 Roo.form.Radio = function(){
43230     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43231 };
43232 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43233     inputType: 'radio',
43234
43235     /**
43236      * If this radio is part of a group, it will return the selected value
43237      * @return {String}
43238      */
43239     getGroupValue : function(){
43240         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43241     },
43242     
43243     
43244     onRender : function(ct, position){
43245         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43246         
43247         if(this.inputValue !== undefined){
43248             this.el.dom.value = this.inputValue;
43249         }
43250          
43251         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43252         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43253         //var viewEl = this.wrap.createChild({ 
43254         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43255         //this.viewEl = viewEl;   
43256         //this.wrap.on('click', this.onClick,  this); 
43257         
43258         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43259         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43260         
43261         
43262         
43263         if(this.boxLabel){
43264             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43265         //    viewEl.on('click', this.onClick,  this); 
43266         }
43267          if(this.checked){
43268             this.el.dom.checked =   'checked' ;
43269         }
43270          
43271     } 
43272     
43273     
43274 });//<script type="text/javascript">
43275
43276 /*
43277  * Based  Ext JS Library 1.1.1
43278  * Copyright(c) 2006-2007, Ext JS, LLC.
43279  * LGPL
43280  *
43281  */
43282  
43283 /**
43284  * @class Roo.HtmlEditorCore
43285  * @extends Roo.Component
43286  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43287  *
43288  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43289  */
43290
43291 Roo.HtmlEditorCore = function(config){
43292     
43293     
43294     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43295     
43296     
43297     this.addEvents({
43298         /**
43299          * @event initialize
43300          * Fires when the editor is fully initialized (including the iframe)
43301          * @param {Roo.HtmlEditorCore} this
43302          */
43303         initialize: true,
43304         /**
43305          * @event activate
43306          * Fires when the editor is first receives the focus. Any insertion must wait
43307          * until after this event.
43308          * @param {Roo.HtmlEditorCore} this
43309          */
43310         activate: true,
43311          /**
43312          * @event beforesync
43313          * Fires before the textarea is updated with content from the editor iframe. Return false
43314          * to cancel the sync.
43315          * @param {Roo.HtmlEditorCore} this
43316          * @param {String} html
43317          */
43318         beforesync: true,
43319          /**
43320          * @event beforepush
43321          * Fires before the iframe editor is updated with content from the textarea. Return false
43322          * to cancel the push.
43323          * @param {Roo.HtmlEditorCore} this
43324          * @param {String} html
43325          */
43326         beforepush: true,
43327          /**
43328          * @event sync
43329          * Fires when the textarea is updated with content from the editor iframe.
43330          * @param {Roo.HtmlEditorCore} this
43331          * @param {String} html
43332          */
43333         sync: true,
43334          /**
43335          * @event push
43336          * Fires when the iframe editor is updated with content from the textarea.
43337          * @param {Roo.HtmlEditorCore} this
43338          * @param {String} html
43339          */
43340         push: true,
43341         
43342         /**
43343          * @event editorevent
43344          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43345          * @param {Roo.HtmlEditorCore} this
43346          */
43347         editorevent: true
43348         
43349     });
43350     
43351     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43352     
43353     // defaults : white / black...
43354     this.applyBlacklists();
43355     
43356     
43357     
43358 };
43359
43360
43361 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43362
43363
43364      /**
43365      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43366      */
43367     
43368     owner : false,
43369     
43370      /**
43371      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43372      *                        Roo.resizable.
43373      */
43374     resizable : false,
43375      /**
43376      * @cfg {Number} height (in pixels)
43377      */   
43378     height: 300,
43379    /**
43380      * @cfg {Number} width (in pixels)
43381      */   
43382     width: 500,
43383     
43384     /**
43385      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43386      * 
43387      */
43388     stylesheets: false,
43389     
43390     // id of frame..
43391     frameId: false,
43392     
43393     // private properties
43394     validationEvent : false,
43395     deferHeight: true,
43396     initialized : false,
43397     activated : false,
43398     sourceEditMode : false,
43399     onFocus : Roo.emptyFn,
43400     iframePad:3,
43401     hideMode:'offsets',
43402     
43403     clearUp: true,
43404     
43405     // blacklist + whitelisted elements..
43406     black: false,
43407     white: false,
43408      
43409     bodyCls : '',
43410
43411     /**
43412      * Protected method that will not generally be called directly. It
43413      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43414      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43415      */
43416     getDocMarkup : function(){
43417         // body styles..
43418         var st = '';
43419         
43420         // inherit styels from page...?? 
43421         if (this.stylesheets === false) {
43422             
43423             Roo.get(document.head).select('style').each(function(node) {
43424                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43425             });
43426             
43427             Roo.get(document.head).select('link').each(function(node) { 
43428                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43429             });
43430             
43431         } else if (!this.stylesheets.length) {
43432                 // simple..
43433                 st = '<style type="text/css">' +
43434                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43435                    '</style>';
43436         } else { 
43437             st = '<style type="text/css">' +
43438                     this.stylesheets +
43439                 '</style>';
43440         }
43441         
43442         st +=  '<style type="text/css">' +
43443             'IMG { cursor: pointer } ' +
43444         '</style>';
43445
43446         var cls = 'roo-htmleditor-body';
43447         
43448         if(this.bodyCls.length){
43449             cls += ' ' + this.bodyCls;
43450         }
43451         
43452         return '<html><head>' + st  +
43453             //<style type="text/css">' +
43454             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43455             //'</style>' +
43456             ' </head><body class="' +  cls + '"></body></html>';
43457     },
43458
43459     // private
43460     onRender : function(ct, position)
43461     {
43462         var _t = this;
43463         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43464         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43465         
43466         
43467         this.el.dom.style.border = '0 none';
43468         this.el.dom.setAttribute('tabIndex', -1);
43469         this.el.addClass('x-hidden hide');
43470         
43471         
43472         
43473         if(Roo.isIE){ // fix IE 1px bogus margin
43474             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43475         }
43476        
43477         
43478         this.frameId = Roo.id();
43479         
43480          
43481         
43482         var iframe = this.owner.wrap.createChild({
43483             tag: 'iframe',
43484             cls: 'form-control', // bootstrap..
43485             id: this.frameId,
43486             name: this.frameId,
43487             frameBorder : 'no',
43488             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43489         }, this.el
43490         );
43491         
43492         
43493         this.iframe = iframe.dom;
43494
43495          this.assignDocWin();
43496         
43497         this.doc.designMode = 'on';
43498        
43499         this.doc.open();
43500         this.doc.write(this.getDocMarkup());
43501         this.doc.close();
43502
43503         
43504         var task = { // must defer to wait for browser to be ready
43505             run : function(){
43506                 //console.log("run task?" + this.doc.readyState);
43507                 this.assignDocWin();
43508                 if(this.doc.body || this.doc.readyState == 'complete'){
43509                     try {
43510                         this.doc.designMode="on";
43511                     } catch (e) {
43512                         return;
43513                     }
43514                     Roo.TaskMgr.stop(task);
43515                     this.initEditor.defer(10, this);
43516                 }
43517             },
43518             interval : 10,
43519             duration: 10000,
43520             scope: this
43521         };
43522         Roo.TaskMgr.start(task);
43523
43524     },
43525
43526     // private
43527     onResize : function(w, h)
43528     {
43529          Roo.log('resize: ' +w + ',' + h );
43530         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43531         if(!this.iframe){
43532             return;
43533         }
43534         if(typeof w == 'number'){
43535             
43536             this.iframe.style.width = w + 'px';
43537         }
43538         if(typeof h == 'number'){
43539             
43540             this.iframe.style.height = h + 'px';
43541             if(this.doc){
43542                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43543             }
43544         }
43545         
43546     },
43547
43548     /**
43549      * Toggles the editor between standard and source edit mode.
43550      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43551      */
43552     toggleSourceEdit : function(sourceEditMode){
43553         
43554         this.sourceEditMode = sourceEditMode === true;
43555         
43556         if(this.sourceEditMode){
43557  
43558             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43559             
43560         }else{
43561             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43562             //this.iframe.className = '';
43563             this.deferFocus();
43564         }
43565         //this.setSize(this.owner.wrap.getSize());
43566         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43567     },
43568
43569     
43570   
43571
43572     /**
43573      * Protected method that will not generally be called directly. If you need/want
43574      * custom HTML cleanup, this is the method you should override.
43575      * @param {String} html The HTML to be cleaned
43576      * return {String} The cleaned HTML
43577      */
43578     cleanHtml : function(html){
43579         html = String(html);
43580         if(html.length > 5){
43581             if(Roo.isSafari){ // strip safari nonsense
43582                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43583             }
43584         }
43585         if(html == '&nbsp;'){
43586             html = '';
43587         }
43588         return html;
43589     },
43590
43591     /**
43592      * HTML Editor -> Textarea
43593      * Protected method that will not generally be called directly. Syncs the contents
43594      * of the editor iframe with the textarea.
43595      */
43596     syncValue : function(){
43597         if(this.initialized){
43598             var bd = (this.doc.body || this.doc.documentElement);
43599             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43600             var html = bd.innerHTML;
43601             if(Roo.isSafari){
43602                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43603                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43604                 if(m && m[1]){
43605                     html = '<div style="'+m[0]+'">' + html + '</div>';
43606                 }
43607             }
43608             html = this.cleanHtml(html);
43609             // fix up the special chars.. normaly like back quotes in word...
43610             // however we do not want to do this with chinese..
43611             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43612                 
43613                 var cc = match.charCodeAt();
43614
43615                 // Get the character value, handling surrogate pairs
43616                 if (match.length == 2) {
43617                     // It's a surrogate pair, calculate the Unicode code point
43618                     var high = match.charCodeAt(0) - 0xD800;
43619                     var low  = match.charCodeAt(1) - 0xDC00;
43620                     cc = (high * 0x400) + low + 0x10000;
43621                 }  else if (
43622                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43623                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43624                     (cc >= 0xf900 && cc < 0xfb00 )
43625                 ) {
43626                         return match;
43627                 }  
43628          
43629                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
43630                 return "&#" + cc + ";";
43631                 
43632                 
43633             });
43634             
43635             
43636              
43637             if(this.owner.fireEvent('beforesync', this, html) !== false){
43638                 this.el.dom.value = html;
43639                 this.owner.fireEvent('sync', this, html);
43640             }
43641         }
43642     },
43643
43644     /**
43645      * Protected method that will not generally be called directly. Pushes the value of the textarea
43646      * into the iframe editor.
43647      */
43648     pushValue : function(){
43649         if(this.initialized){
43650             var v = this.el.dom.value.trim();
43651             
43652 //            if(v.length < 1){
43653 //                v = '&#160;';
43654 //            }
43655             
43656             if(this.owner.fireEvent('beforepush', this, v) !== false){
43657                 var d = (this.doc.body || this.doc.documentElement);
43658                 d.innerHTML = v;
43659                 this.cleanUpPaste();
43660                 this.el.dom.value = d.innerHTML;
43661                 this.owner.fireEvent('push', this, v);
43662             }
43663         }
43664     },
43665
43666     // private
43667     deferFocus : function(){
43668         this.focus.defer(10, this);
43669     },
43670
43671     // doc'ed in Field
43672     focus : function(){
43673         if(this.win && !this.sourceEditMode){
43674             this.win.focus();
43675         }else{
43676             this.el.focus();
43677         }
43678     },
43679     
43680     assignDocWin: function()
43681     {
43682         var iframe = this.iframe;
43683         
43684          if(Roo.isIE){
43685             this.doc = iframe.contentWindow.document;
43686             this.win = iframe.contentWindow;
43687         } else {
43688 //            if (!Roo.get(this.frameId)) {
43689 //                return;
43690 //            }
43691 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43692 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43693             
43694             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43695                 return;
43696             }
43697             
43698             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43699             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43700         }
43701     },
43702     
43703     // private
43704     initEditor : function(){
43705         //console.log("INIT EDITOR");
43706         this.assignDocWin();
43707         
43708         
43709         
43710         this.doc.designMode="on";
43711         this.doc.open();
43712         this.doc.write(this.getDocMarkup());
43713         this.doc.close();
43714         
43715         var dbody = (this.doc.body || this.doc.documentElement);
43716         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43717         // this copies styles from the containing element into thsi one..
43718         // not sure why we need all of this..
43719         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43720         
43721         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43722         //ss['background-attachment'] = 'fixed'; // w3c
43723         dbody.bgProperties = 'fixed'; // ie
43724         //Roo.DomHelper.applyStyles(dbody, ss);
43725         Roo.EventManager.on(this.doc, {
43726             //'mousedown': this.onEditorEvent,
43727             'mouseup': this.onEditorEvent,
43728             'dblclick': this.onEditorEvent,
43729             'click': this.onEditorEvent,
43730             'keyup': this.onEditorEvent,
43731             buffer:100,
43732             scope: this
43733         });
43734         if(Roo.isGecko){
43735             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43736         }
43737         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43738             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43739         }
43740         this.initialized = true;
43741
43742         this.owner.fireEvent('initialize', this);
43743         this.pushValue();
43744     },
43745
43746     // private
43747     onDestroy : function(){
43748         
43749         
43750         
43751         if(this.rendered){
43752             
43753             //for (var i =0; i < this.toolbars.length;i++) {
43754             //    // fixme - ask toolbars for heights?
43755             //    this.toolbars[i].onDestroy();
43756            // }
43757             
43758             //this.wrap.dom.innerHTML = '';
43759             //this.wrap.remove();
43760         }
43761     },
43762
43763     // private
43764     onFirstFocus : function(){
43765         
43766         this.assignDocWin();
43767         
43768         
43769         this.activated = true;
43770          
43771     
43772         if(Roo.isGecko){ // prevent silly gecko errors
43773             this.win.focus();
43774             var s = this.win.getSelection();
43775             if(!s.focusNode || s.focusNode.nodeType != 3){
43776                 var r = s.getRangeAt(0);
43777                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43778                 r.collapse(true);
43779                 this.deferFocus();
43780             }
43781             try{
43782                 this.execCmd('useCSS', true);
43783                 this.execCmd('styleWithCSS', false);
43784             }catch(e){}
43785         }
43786         this.owner.fireEvent('activate', this);
43787     },
43788
43789     // private
43790     adjustFont: function(btn){
43791         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43792         //if(Roo.isSafari){ // safari
43793         //    adjust *= 2;
43794        // }
43795         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43796         if(Roo.isSafari){ // safari
43797             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43798             v =  (v < 10) ? 10 : v;
43799             v =  (v > 48) ? 48 : v;
43800             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43801             
43802         }
43803         
43804         
43805         v = Math.max(1, v+adjust);
43806         
43807         this.execCmd('FontSize', v  );
43808     },
43809
43810     onEditorEvent : function(e)
43811     {
43812         this.owner.fireEvent('editorevent', this, e);
43813       //  this.updateToolbar();
43814         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43815     },
43816
43817     insertTag : function(tg)
43818     {
43819         // could be a bit smarter... -> wrap the current selected tRoo..
43820         if (tg.toLowerCase() == 'span' ||
43821             tg.toLowerCase() == 'code' ||
43822             tg.toLowerCase() == 'sup' ||
43823             tg.toLowerCase() == 'sub' 
43824             ) {
43825             
43826             range = this.createRange(this.getSelection());
43827             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43828             wrappingNode.appendChild(range.extractContents());
43829             range.insertNode(wrappingNode);
43830
43831             return;
43832             
43833             
43834             
43835         }
43836         this.execCmd("formatblock",   tg);
43837         
43838     },
43839     
43840     insertText : function(txt)
43841     {
43842         
43843         
43844         var range = this.createRange();
43845         range.deleteContents();
43846                //alert(Sender.getAttribute('label'));
43847                
43848         range.insertNode(this.doc.createTextNode(txt));
43849     } ,
43850     
43851      
43852
43853     /**
43854      * Executes a Midas editor command on the editor document and performs necessary focus and
43855      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43856      * @param {String} cmd The Midas command
43857      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43858      */
43859     relayCmd : function(cmd, value){
43860         this.win.focus();
43861         this.execCmd(cmd, value);
43862         this.owner.fireEvent('editorevent', this);
43863         //this.updateToolbar();
43864         this.owner.deferFocus();
43865     },
43866
43867     /**
43868      * Executes a Midas editor command directly on the editor document.
43869      * For visual commands, you should use {@link #relayCmd} instead.
43870      * <b>This should only be called after the editor is initialized.</b>
43871      * @param {String} cmd The Midas command
43872      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43873      */
43874     execCmd : function(cmd, value){
43875         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43876         this.syncValue();
43877     },
43878  
43879  
43880    
43881     /**
43882      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43883      * to insert tRoo.
43884      * @param {String} text | dom node.. 
43885      */
43886     insertAtCursor : function(text)
43887     {
43888         
43889         if(!this.activated){
43890             return;
43891         }
43892         /*
43893         if(Roo.isIE){
43894             this.win.focus();
43895             var r = this.doc.selection.createRange();
43896             if(r){
43897                 r.collapse(true);
43898                 r.pasteHTML(text);
43899                 this.syncValue();
43900                 this.deferFocus();
43901             
43902             }
43903             return;
43904         }
43905         */
43906         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43907             this.win.focus();
43908             
43909             
43910             // from jquery ui (MIT licenced)
43911             var range, node;
43912             var win = this.win;
43913             
43914             if (win.getSelection && win.getSelection().getRangeAt) {
43915                 range = win.getSelection().getRangeAt(0);
43916                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43917                 range.insertNode(node);
43918             } else if (win.document.selection && win.document.selection.createRange) {
43919                 // no firefox support
43920                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43921                 win.document.selection.createRange().pasteHTML(txt);
43922             } else {
43923                 // no firefox support
43924                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43925                 this.execCmd('InsertHTML', txt);
43926             } 
43927             
43928             this.syncValue();
43929             
43930             this.deferFocus();
43931         }
43932     },
43933  // private
43934     mozKeyPress : function(e){
43935         if(e.ctrlKey){
43936             var c = e.getCharCode(), cmd;
43937           
43938             if(c > 0){
43939                 c = String.fromCharCode(c).toLowerCase();
43940                 switch(c){
43941                     case 'b':
43942                         cmd = 'bold';
43943                         break;
43944                     case 'i':
43945                         cmd = 'italic';
43946                         break;
43947                     
43948                     case 'u':
43949                         cmd = 'underline';
43950                         break;
43951                     
43952                     case 'v':
43953                         this.cleanUpPaste.defer(100, this);
43954                         return;
43955                         
43956                 }
43957                 if(cmd){
43958                     this.win.focus();
43959                     this.execCmd(cmd);
43960                     this.deferFocus();
43961                     e.preventDefault();
43962                 }
43963                 
43964             }
43965         }
43966     },
43967
43968     // private
43969     fixKeys : function(){ // load time branching for fastest keydown performance
43970         if(Roo.isIE){
43971             return function(e){
43972                 var k = e.getKey(), r;
43973                 if(k == e.TAB){
43974                     e.stopEvent();
43975                     r = this.doc.selection.createRange();
43976                     if(r){
43977                         r.collapse(true);
43978                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43979                         this.deferFocus();
43980                     }
43981                     return;
43982                 }
43983                 
43984                 if(k == e.ENTER){
43985                     r = this.doc.selection.createRange();
43986                     if(r){
43987                         var target = r.parentElement();
43988                         if(!target || target.tagName.toLowerCase() != 'li'){
43989                             e.stopEvent();
43990                             r.pasteHTML('<br />');
43991                             r.collapse(false);
43992                             r.select();
43993                         }
43994                     }
43995                 }
43996                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43997                     this.cleanUpPaste.defer(100, this);
43998                     return;
43999                 }
44000                 
44001                 
44002             };
44003         }else if(Roo.isOpera){
44004             return function(e){
44005                 var k = e.getKey();
44006                 if(k == e.TAB){
44007                     e.stopEvent();
44008                     this.win.focus();
44009                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44010                     this.deferFocus();
44011                 }
44012                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44013                     this.cleanUpPaste.defer(100, this);
44014                     return;
44015                 }
44016                 
44017             };
44018         }else if(Roo.isSafari){
44019             return function(e){
44020                 var k = e.getKey();
44021                 
44022                 if(k == e.TAB){
44023                     e.stopEvent();
44024                     this.execCmd('InsertText','\t');
44025                     this.deferFocus();
44026                     return;
44027                 }
44028                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44029                     this.cleanUpPaste.defer(100, this);
44030                     return;
44031                 }
44032                 
44033              };
44034         }
44035     }(),
44036     
44037     getAllAncestors: function()
44038     {
44039         var p = this.getSelectedNode();
44040         var a = [];
44041         if (!p) {
44042             a.push(p); // push blank onto stack..
44043             p = this.getParentElement();
44044         }
44045         
44046         
44047         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44048             a.push(p);
44049             p = p.parentNode;
44050         }
44051         a.push(this.doc.body);
44052         return a;
44053     },
44054     lastSel : false,
44055     lastSelNode : false,
44056     
44057     
44058     getSelection : function() 
44059     {
44060         this.assignDocWin();
44061         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44062     },
44063     
44064     getSelectedNode: function() 
44065     {
44066         // this may only work on Gecko!!!
44067         
44068         // should we cache this!!!!
44069         
44070         
44071         
44072          
44073         var range = this.createRange(this.getSelection()).cloneRange();
44074         
44075         if (Roo.isIE) {
44076             var parent = range.parentElement();
44077             while (true) {
44078                 var testRange = range.duplicate();
44079                 testRange.moveToElementText(parent);
44080                 if (testRange.inRange(range)) {
44081                     break;
44082                 }
44083                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44084                     break;
44085                 }
44086                 parent = parent.parentElement;
44087             }
44088             return parent;
44089         }
44090         
44091         // is ancestor a text element.
44092         var ac =  range.commonAncestorContainer;
44093         if (ac.nodeType == 3) {
44094             ac = ac.parentNode;
44095         }
44096         
44097         var ar = ac.childNodes;
44098          
44099         var nodes = [];
44100         var other_nodes = [];
44101         var has_other_nodes = false;
44102         for (var i=0;i<ar.length;i++) {
44103             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44104                 continue;
44105             }
44106             // fullly contained node.
44107             
44108             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44109                 nodes.push(ar[i]);
44110                 continue;
44111             }
44112             
44113             // probably selected..
44114             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44115                 other_nodes.push(ar[i]);
44116                 continue;
44117             }
44118             // outer..
44119             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44120                 continue;
44121             }
44122             
44123             
44124             has_other_nodes = true;
44125         }
44126         if (!nodes.length && other_nodes.length) {
44127             nodes= other_nodes;
44128         }
44129         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44130             return false;
44131         }
44132         
44133         return nodes[0];
44134     },
44135     createRange: function(sel)
44136     {
44137         // this has strange effects when using with 
44138         // top toolbar - not sure if it's a great idea.
44139         //this.editor.contentWindow.focus();
44140         if (typeof sel != "undefined") {
44141             try {
44142                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44143             } catch(e) {
44144                 return this.doc.createRange();
44145             }
44146         } else {
44147             return this.doc.createRange();
44148         }
44149     },
44150     getParentElement: function()
44151     {
44152         
44153         this.assignDocWin();
44154         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44155         
44156         var range = this.createRange(sel);
44157          
44158         try {
44159             var p = range.commonAncestorContainer;
44160             while (p.nodeType == 3) { // text node
44161                 p = p.parentNode;
44162             }
44163             return p;
44164         } catch (e) {
44165             return null;
44166         }
44167     
44168     },
44169     /***
44170      *
44171      * Range intersection.. the hard stuff...
44172      *  '-1' = before
44173      *  '0' = hits..
44174      *  '1' = after.
44175      *         [ -- selected range --- ]
44176      *   [fail]                        [fail]
44177      *
44178      *    basically..
44179      *      if end is before start or  hits it. fail.
44180      *      if start is after end or hits it fail.
44181      *
44182      *   if either hits (but other is outside. - then it's not 
44183      *   
44184      *    
44185      **/
44186     
44187     
44188     // @see http://www.thismuchiknow.co.uk/?p=64.
44189     rangeIntersectsNode : function(range, node)
44190     {
44191         var nodeRange = node.ownerDocument.createRange();
44192         try {
44193             nodeRange.selectNode(node);
44194         } catch (e) {
44195             nodeRange.selectNodeContents(node);
44196         }
44197     
44198         var rangeStartRange = range.cloneRange();
44199         rangeStartRange.collapse(true);
44200     
44201         var rangeEndRange = range.cloneRange();
44202         rangeEndRange.collapse(false);
44203     
44204         var nodeStartRange = nodeRange.cloneRange();
44205         nodeStartRange.collapse(true);
44206     
44207         var nodeEndRange = nodeRange.cloneRange();
44208         nodeEndRange.collapse(false);
44209     
44210         return rangeStartRange.compareBoundaryPoints(
44211                  Range.START_TO_START, nodeEndRange) == -1 &&
44212                rangeEndRange.compareBoundaryPoints(
44213                  Range.START_TO_START, nodeStartRange) == 1;
44214         
44215          
44216     },
44217     rangeCompareNode : function(range, node)
44218     {
44219         var nodeRange = node.ownerDocument.createRange();
44220         try {
44221             nodeRange.selectNode(node);
44222         } catch (e) {
44223             nodeRange.selectNodeContents(node);
44224         }
44225         
44226         
44227         range.collapse(true);
44228     
44229         nodeRange.collapse(true);
44230      
44231         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44232         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44233          
44234         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44235         
44236         var nodeIsBefore   =  ss == 1;
44237         var nodeIsAfter    = ee == -1;
44238         
44239         if (nodeIsBefore && nodeIsAfter) {
44240             return 0; // outer
44241         }
44242         if (!nodeIsBefore && nodeIsAfter) {
44243             return 1; //right trailed.
44244         }
44245         
44246         if (nodeIsBefore && !nodeIsAfter) {
44247             return 2;  // left trailed.
44248         }
44249         // fully contined.
44250         return 3;
44251     },
44252
44253     // private? - in a new class?
44254     cleanUpPaste :  function()
44255     {
44256         // cleans up the whole document..
44257         Roo.log('cleanuppaste');
44258         
44259         this.cleanUpChildren(this.doc.body);
44260         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44261         if (clean != this.doc.body.innerHTML) {
44262             this.doc.body.innerHTML = clean;
44263         }
44264         
44265     },
44266     
44267     cleanWordChars : function(input) {// change the chars to hex code
44268         var he = Roo.HtmlEditorCore;
44269         
44270         var output = input;
44271         Roo.each(he.swapCodes, function(sw) { 
44272             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44273             
44274             output = output.replace(swapper, sw[1]);
44275         });
44276         
44277         return output;
44278     },
44279     
44280     
44281     cleanUpChildren : function (n)
44282     {
44283         if (!n.childNodes.length) {
44284             return;
44285         }
44286         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44287            this.cleanUpChild(n.childNodes[i]);
44288         }
44289     },
44290     
44291     
44292         
44293     
44294     cleanUpChild : function (node)
44295     {
44296         var ed = this;
44297         //console.log(node);
44298         if (node.nodeName == "#text") {
44299             // clean up silly Windows -- stuff?
44300             return; 
44301         }
44302         if (node.nodeName == "#comment") {
44303             node.parentNode.removeChild(node);
44304             // clean up silly Windows -- stuff?
44305             return; 
44306         }
44307         var lcname = node.tagName.toLowerCase();
44308         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44309         // whitelist of tags..
44310         
44311         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44312             // remove node.
44313             node.parentNode.removeChild(node);
44314             return;
44315             
44316         }
44317         
44318         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44319         
44320         // spans with no attributes - just remove them..
44321         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44322             remove_keep_children = true;
44323         }
44324         
44325         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44326         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44327         
44328         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44329         //    remove_keep_children = true;
44330         //}
44331         
44332         if (remove_keep_children) {
44333             this.cleanUpChildren(node);
44334             // inserts everything just before this node...
44335             while (node.childNodes.length) {
44336                 var cn = node.childNodes[0];
44337                 node.removeChild(cn);
44338                 node.parentNode.insertBefore(cn, node);
44339             }
44340             node.parentNode.removeChild(node);
44341             return;
44342         }
44343         
44344         if (!node.attributes || !node.attributes.length) {
44345             
44346           
44347             
44348             
44349             this.cleanUpChildren(node);
44350             return;
44351         }
44352         
44353         function cleanAttr(n,v)
44354         {
44355             
44356             if (v.match(/^\./) || v.match(/^\//)) {
44357                 return;
44358             }
44359             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44360                 return;
44361             }
44362             if (v.match(/^#/)) {
44363                 return;
44364             }
44365 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44366             node.removeAttribute(n);
44367             
44368         }
44369         
44370         var cwhite = this.cwhite;
44371         var cblack = this.cblack;
44372             
44373         function cleanStyle(n,v)
44374         {
44375             if (v.match(/expression/)) { //XSS?? should we even bother..
44376                 node.removeAttribute(n);
44377                 return;
44378             }
44379             
44380             var parts = v.split(/;/);
44381             var clean = [];
44382             
44383             Roo.each(parts, function(p) {
44384                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44385                 if (!p.length) {
44386                     return true;
44387                 }
44388                 var l = p.split(':').shift().replace(/\s+/g,'');
44389                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44390                 
44391                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44392 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44393                     //node.removeAttribute(n);
44394                     return true;
44395                 }
44396                 //Roo.log()
44397                 // only allow 'c whitelisted system attributes'
44398                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44399 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44400                     //node.removeAttribute(n);
44401                     return true;
44402                 }
44403                 
44404                 
44405                  
44406                 
44407                 clean.push(p);
44408                 return true;
44409             });
44410             if (clean.length) { 
44411                 node.setAttribute(n, clean.join(';'));
44412             } else {
44413                 node.removeAttribute(n);
44414             }
44415             
44416         }
44417         
44418         
44419         for (var i = node.attributes.length-1; i > -1 ; i--) {
44420             var a = node.attributes[i];
44421             //console.log(a);
44422             
44423             if (a.name.toLowerCase().substr(0,2)=='on')  {
44424                 node.removeAttribute(a.name);
44425                 continue;
44426             }
44427             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44428                 node.removeAttribute(a.name);
44429                 continue;
44430             }
44431             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44432                 cleanAttr(a.name,a.value); // fixme..
44433                 continue;
44434             }
44435             if (a.name == 'style') {
44436                 cleanStyle(a.name,a.value);
44437                 continue;
44438             }
44439             /// clean up MS crap..
44440             // tecnically this should be a list of valid class'es..
44441             
44442             
44443             if (a.name == 'class') {
44444                 if (a.value.match(/^Mso/)) {
44445                     node.removeAttribute('class');
44446                 }
44447                 
44448                 if (a.value.match(/^body$/)) {
44449                     node.removeAttribute('class');
44450                 }
44451                 continue;
44452             }
44453             
44454             // style cleanup!?
44455             // class cleanup?
44456             
44457         }
44458         
44459         
44460         this.cleanUpChildren(node);
44461         
44462         
44463     },
44464     
44465     /**
44466      * Clean up MS wordisms...
44467      */
44468     cleanWord : function(node)
44469     {
44470         if (!node) {
44471             this.cleanWord(this.doc.body);
44472             return;
44473         }
44474         
44475         if(
44476                 node.nodeName == 'SPAN' &&
44477                 !node.hasAttributes() &&
44478                 node.childNodes.length == 1 &&
44479                 node.firstChild.nodeName == "#text"  
44480         ) {
44481             var textNode = node.firstChild;
44482             node.removeChild(textNode);
44483             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44484                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44485             }
44486             node.parentNode.insertBefore(textNode, node);
44487             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44488                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44489             }
44490             node.parentNode.removeChild(node);
44491         }
44492         
44493         if (node.nodeName == "#text") {
44494             // clean up silly Windows -- stuff?
44495             return; 
44496         }
44497         if (node.nodeName == "#comment") {
44498             node.parentNode.removeChild(node);
44499             // clean up silly Windows -- stuff?
44500             return; 
44501         }
44502         
44503         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44504             node.parentNode.removeChild(node);
44505             return;
44506         }
44507         //Roo.log(node.tagName);
44508         // remove - but keep children..
44509         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44510             //Roo.log('-- removed');
44511             while (node.childNodes.length) {
44512                 var cn = node.childNodes[0];
44513                 node.removeChild(cn);
44514                 node.parentNode.insertBefore(cn, node);
44515                 // move node to parent - and clean it..
44516                 this.cleanWord(cn);
44517             }
44518             node.parentNode.removeChild(node);
44519             /// no need to iterate chidlren = it's got none..
44520             //this.iterateChildren(node, this.cleanWord);
44521             return;
44522         }
44523         // clean styles
44524         if (node.className.length) {
44525             
44526             var cn = node.className.split(/\W+/);
44527             var cna = [];
44528             Roo.each(cn, function(cls) {
44529                 if (cls.match(/Mso[a-zA-Z]+/)) {
44530                     return;
44531                 }
44532                 cna.push(cls);
44533             });
44534             node.className = cna.length ? cna.join(' ') : '';
44535             if (!cna.length) {
44536                 node.removeAttribute("class");
44537             }
44538         }
44539         
44540         if (node.hasAttribute("lang")) {
44541             node.removeAttribute("lang");
44542         }
44543         
44544         if (node.hasAttribute("style")) {
44545             
44546             var styles = node.getAttribute("style").split(";");
44547             var nstyle = [];
44548             Roo.each(styles, function(s) {
44549                 if (!s.match(/:/)) {
44550                     return;
44551                 }
44552                 var kv = s.split(":");
44553                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44554                     return;
44555                 }
44556                 // what ever is left... we allow.
44557                 nstyle.push(s);
44558             });
44559             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44560             if (!nstyle.length) {
44561                 node.removeAttribute('style');
44562             }
44563         }
44564         this.iterateChildren(node, this.cleanWord);
44565         
44566         
44567         
44568     },
44569     /**
44570      * iterateChildren of a Node, calling fn each time, using this as the scole..
44571      * @param {DomNode} node node to iterate children of.
44572      * @param {Function} fn method of this class to call on each item.
44573      */
44574     iterateChildren : function(node, fn)
44575     {
44576         if (!node.childNodes.length) {
44577                 return;
44578         }
44579         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44580            fn.call(this, node.childNodes[i])
44581         }
44582     },
44583     
44584     
44585     /**
44586      * cleanTableWidths.
44587      *
44588      * Quite often pasting from word etc.. results in tables with column and widths.
44589      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44590      *
44591      */
44592     cleanTableWidths : function(node)
44593     {
44594          
44595          
44596         if (!node) {
44597             this.cleanTableWidths(this.doc.body);
44598             return;
44599         }
44600         
44601         // ignore list...
44602         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44603             return; 
44604         }
44605         Roo.log(node.tagName);
44606         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44607             this.iterateChildren(node, this.cleanTableWidths);
44608             return;
44609         }
44610         if (node.hasAttribute('width')) {
44611             node.removeAttribute('width');
44612         }
44613         
44614          
44615         if (node.hasAttribute("style")) {
44616             // pretty basic...
44617             
44618             var styles = node.getAttribute("style").split(";");
44619             var nstyle = [];
44620             Roo.each(styles, function(s) {
44621                 if (!s.match(/:/)) {
44622                     return;
44623                 }
44624                 var kv = s.split(":");
44625                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44626                     return;
44627                 }
44628                 // what ever is left... we allow.
44629                 nstyle.push(s);
44630             });
44631             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44632             if (!nstyle.length) {
44633                 node.removeAttribute('style');
44634             }
44635         }
44636         
44637         this.iterateChildren(node, this.cleanTableWidths);
44638         
44639         
44640     },
44641     
44642     
44643     
44644     
44645     domToHTML : function(currentElement, depth, nopadtext) {
44646         
44647         depth = depth || 0;
44648         nopadtext = nopadtext || false;
44649     
44650         if (!currentElement) {
44651             return this.domToHTML(this.doc.body);
44652         }
44653         
44654         //Roo.log(currentElement);
44655         var j;
44656         var allText = false;
44657         var nodeName = currentElement.nodeName;
44658         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44659         
44660         if  (nodeName == '#text') {
44661             
44662             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44663         }
44664         
44665         
44666         var ret = '';
44667         if (nodeName != 'BODY') {
44668              
44669             var i = 0;
44670             // Prints the node tagName, such as <A>, <IMG>, etc
44671             if (tagName) {
44672                 var attr = [];
44673                 for(i = 0; i < currentElement.attributes.length;i++) {
44674                     // quoting?
44675                     var aname = currentElement.attributes.item(i).name;
44676                     if (!currentElement.attributes.item(i).value.length) {
44677                         continue;
44678                     }
44679                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44680                 }
44681                 
44682                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44683             } 
44684             else {
44685                 
44686                 // eack
44687             }
44688         } else {
44689             tagName = false;
44690         }
44691         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44692             return ret;
44693         }
44694         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44695             nopadtext = true;
44696         }
44697         
44698         
44699         // Traverse the tree
44700         i = 0;
44701         var currentElementChild = currentElement.childNodes.item(i);
44702         var allText = true;
44703         var innerHTML  = '';
44704         lastnode = '';
44705         while (currentElementChild) {
44706             // Formatting code (indent the tree so it looks nice on the screen)
44707             var nopad = nopadtext;
44708             if (lastnode == 'SPAN') {
44709                 nopad  = true;
44710             }
44711             // text
44712             if  (currentElementChild.nodeName == '#text') {
44713                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44714                 toadd = nopadtext ? toadd : toadd.trim();
44715                 if (!nopad && toadd.length > 80) {
44716                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44717                 }
44718                 innerHTML  += toadd;
44719                 
44720                 i++;
44721                 currentElementChild = currentElement.childNodes.item(i);
44722                 lastNode = '';
44723                 continue;
44724             }
44725             allText = false;
44726             
44727             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44728                 
44729             // Recursively traverse the tree structure of the child node
44730             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44731             lastnode = currentElementChild.nodeName;
44732             i++;
44733             currentElementChild=currentElement.childNodes.item(i);
44734         }
44735         
44736         ret += innerHTML;
44737         
44738         if (!allText) {
44739                 // The remaining code is mostly for formatting the tree
44740             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44741         }
44742         
44743         
44744         if (tagName) {
44745             ret+= "</"+tagName+">";
44746         }
44747         return ret;
44748         
44749     },
44750         
44751     applyBlacklists : function()
44752     {
44753         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44754         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44755         
44756         this.white = [];
44757         this.black = [];
44758         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44759             if (b.indexOf(tag) > -1) {
44760                 return;
44761             }
44762             this.white.push(tag);
44763             
44764         }, this);
44765         
44766         Roo.each(w, function(tag) {
44767             if (b.indexOf(tag) > -1) {
44768                 return;
44769             }
44770             if (this.white.indexOf(tag) > -1) {
44771                 return;
44772             }
44773             this.white.push(tag);
44774             
44775         }, this);
44776         
44777         
44778         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44779             if (w.indexOf(tag) > -1) {
44780                 return;
44781             }
44782             this.black.push(tag);
44783             
44784         }, this);
44785         
44786         Roo.each(b, function(tag) {
44787             if (w.indexOf(tag) > -1) {
44788                 return;
44789             }
44790             if (this.black.indexOf(tag) > -1) {
44791                 return;
44792             }
44793             this.black.push(tag);
44794             
44795         }, this);
44796         
44797         
44798         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44799         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44800         
44801         this.cwhite = [];
44802         this.cblack = [];
44803         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44804             if (b.indexOf(tag) > -1) {
44805                 return;
44806             }
44807             this.cwhite.push(tag);
44808             
44809         }, this);
44810         
44811         Roo.each(w, function(tag) {
44812             if (b.indexOf(tag) > -1) {
44813                 return;
44814             }
44815             if (this.cwhite.indexOf(tag) > -1) {
44816                 return;
44817             }
44818             this.cwhite.push(tag);
44819             
44820         }, this);
44821         
44822         
44823         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44824             if (w.indexOf(tag) > -1) {
44825                 return;
44826             }
44827             this.cblack.push(tag);
44828             
44829         }, this);
44830         
44831         Roo.each(b, function(tag) {
44832             if (w.indexOf(tag) > -1) {
44833                 return;
44834             }
44835             if (this.cblack.indexOf(tag) > -1) {
44836                 return;
44837             }
44838             this.cblack.push(tag);
44839             
44840         }, this);
44841     },
44842     
44843     setStylesheets : function(stylesheets)
44844     {
44845         if(typeof(stylesheets) == 'string'){
44846             Roo.get(this.iframe.contentDocument.head).createChild({
44847                 tag : 'link',
44848                 rel : 'stylesheet',
44849                 type : 'text/css',
44850                 href : stylesheets
44851             });
44852             
44853             return;
44854         }
44855         var _this = this;
44856      
44857         Roo.each(stylesheets, function(s) {
44858             if(!s.length){
44859                 return;
44860             }
44861             
44862             Roo.get(_this.iframe.contentDocument.head).createChild({
44863                 tag : 'link',
44864                 rel : 'stylesheet',
44865                 type : 'text/css',
44866                 href : s
44867             });
44868         });
44869
44870         
44871     },
44872     
44873     removeStylesheets : function()
44874     {
44875         var _this = this;
44876         
44877         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44878             s.remove();
44879         });
44880     },
44881     
44882     setStyle : function(style)
44883     {
44884         Roo.get(this.iframe.contentDocument.head).createChild({
44885             tag : 'style',
44886             type : 'text/css',
44887             html : style
44888         });
44889
44890         return;
44891     }
44892     
44893     // hide stuff that is not compatible
44894     /**
44895      * @event blur
44896      * @hide
44897      */
44898     /**
44899      * @event change
44900      * @hide
44901      */
44902     /**
44903      * @event focus
44904      * @hide
44905      */
44906     /**
44907      * @event specialkey
44908      * @hide
44909      */
44910     /**
44911      * @cfg {String} fieldClass @hide
44912      */
44913     /**
44914      * @cfg {String} focusClass @hide
44915      */
44916     /**
44917      * @cfg {String} autoCreate @hide
44918      */
44919     /**
44920      * @cfg {String} inputType @hide
44921      */
44922     /**
44923      * @cfg {String} invalidClass @hide
44924      */
44925     /**
44926      * @cfg {String} invalidText @hide
44927      */
44928     /**
44929      * @cfg {String} msgFx @hide
44930      */
44931     /**
44932      * @cfg {String} validateOnBlur @hide
44933      */
44934 });
44935
44936 Roo.HtmlEditorCore.white = [
44937         'area', 'br', 'img', 'input', 'hr', 'wbr',
44938         
44939        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44940        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44941        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44942        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44943        'table',   'ul',         'xmp', 
44944        
44945        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44946       'thead',   'tr', 
44947      
44948       'dir', 'menu', 'ol', 'ul', 'dl',
44949        
44950       'embed',  'object'
44951 ];
44952
44953
44954 Roo.HtmlEditorCore.black = [
44955     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44956         'applet', // 
44957         'base',   'basefont', 'bgsound', 'blink',  'body', 
44958         'frame',  'frameset', 'head',    'html',   'ilayer', 
44959         'iframe', 'layer',  'link',     'meta',    'object',   
44960         'script', 'style' ,'title',  'xml' // clean later..
44961 ];
44962 Roo.HtmlEditorCore.clean = [
44963     'script', 'style', 'title', 'xml'
44964 ];
44965 Roo.HtmlEditorCore.remove = [
44966     'font'
44967 ];
44968 // attributes..
44969
44970 Roo.HtmlEditorCore.ablack = [
44971     'on'
44972 ];
44973     
44974 Roo.HtmlEditorCore.aclean = [ 
44975     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44976 ];
44977
44978 // protocols..
44979 Roo.HtmlEditorCore.pwhite= [
44980         'http',  'https',  'mailto'
44981 ];
44982
44983 // white listed style attributes.
44984 Roo.HtmlEditorCore.cwhite= [
44985       //  'text-align', /// default is to allow most things..
44986       
44987          
44988 //        'font-size'//??
44989 ];
44990
44991 // black listed style attributes.
44992 Roo.HtmlEditorCore.cblack= [
44993       //  'font-size' -- this can be set by the project 
44994 ];
44995
44996
44997 Roo.HtmlEditorCore.swapCodes   =[ 
44998     [    8211, "--" ], 
44999     [    8212, "--" ], 
45000     [    8216,  "'" ],  
45001     [    8217, "'" ],  
45002     [    8220, '"' ],  
45003     [    8221, '"' ],  
45004     [    8226, "*" ],  
45005     [    8230, "..." ]
45006 ]; 
45007
45008     //<script type="text/javascript">
45009
45010 /*
45011  * Ext JS Library 1.1.1
45012  * Copyright(c) 2006-2007, Ext JS, LLC.
45013  * Licence LGPL
45014  * 
45015  */
45016  
45017  
45018 Roo.form.HtmlEditor = function(config){
45019     
45020     
45021     
45022     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45023     
45024     if (!this.toolbars) {
45025         this.toolbars = [];
45026     }
45027     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45028     
45029     
45030 };
45031
45032 /**
45033  * @class Roo.form.HtmlEditor
45034  * @extends Roo.form.Field
45035  * Provides a lightweight HTML Editor component.
45036  *
45037  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45038  * 
45039  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45040  * supported by this editor.</b><br/><br/>
45041  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45042  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45043  */
45044 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45045     /**
45046      * @cfg {Boolean} clearUp
45047      */
45048     clearUp : true,
45049       /**
45050      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45051      */
45052     toolbars : false,
45053    
45054      /**
45055      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45056      *                        Roo.resizable.
45057      */
45058     resizable : false,
45059      /**
45060      * @cfg {Number} height (in pixels)
45061      */   
45062     height: 300,
45063    /**
45064      * @cfg {Number} width (in pixels)
45065      */   
45066     width: 500,
45067     
45068     /**
45069      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45070      * 
45071      */
45072     stylesheets: false,
45073     
45074     
45075      /**
45076      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45077      * 
45078      */
45079     cblack: false,
45080     /**
45081      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45082      * 
45083      */
45084     cwhite: false,
45085     
45086      /**
45087      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45088      * 
45089      */
45090     black: false,
45091     /**
45092      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45093      * 
45094      */
45095     white: false,
45096     
45097     // id of frame..
45098     frameId: false,
45099     
45100     // private properties
45101     validationEvent : false,
45102     deferHeight: true,
45103     initialized : false,
45104     activated : false,
45105     
45106     onFocus : Roo.emptyFn,
45107     iframePad:3,
45108     hideMode:'offsets',
45109     
45110     actionMode : 'container', // defaults to hiding it...
45111     
45112     defaultAutoCreate : { // modified by initCompnoent..
45113         tag: "textarea",
45114         style:"width:500px;height:300px;",
45115         autocomplete: "new-password"
45116     },
45117
45118     // private
45119     initComponent : function(){
45120         this.addEvents({
45121             /**
45122              * @event initialize
45123              * Fires when the editor is fully initialized (including the iframe)
45124              * @param {HtmlEditor} this
45125              */
45126             initialize: true,
45127             /**
45128              * @event activate
45129              * Fires when the editor is first receives the focus. Any insertion must wait
45130              * until after this event.
45131              * @param {HtmlEditor} this
45132              */
45133             activate: true,
45134              /**
45135              * @event beforesync
45136              * Fires before the textarea is updated with content from the editor iframe. Return false
45137              * to cancel the sync.
45138              * @param {HtmlEditor} this
45139              * @param {String} html
45140              */
45141             beforesync: true,
45142              /**
45143              * @event beforepush
45144              * Fires before the iframe editor is updated with content from the textarea. Return false
45145              * to cancel the push.
45146              * @param {HtmlEditor} this
45147              * @param {String} html
45148              */
45149             beforepush: true,
45150              /**
45151              * @event sync
45152              * Fires when the textarea is updated with content from the editor iframe.
45153              * @param {HtmlEditor} this
45154              * @param {String} html
45155              */
45156             sync: true,
45157              /**
45158              * @event push
45159              * Fires when the iframe editor is updated with content from the textarea.
45160              * @param {HtmlEditor} this
45161              * @param {String} html
45162              */
45163             push: true,
45164              /**
45165              * @event editmodechange
45166              * Fires when the editor switches edit modes
45167              * @param {HtmlEditor} this
45168              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45169              */
45170             editmodechange: true,
45171             /**
45172              * @event editorevent
45173              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45174              * @param {HtmlEditor} this
45175              */
45176             editorevent: true,
45177             /**
45178              * @event firstfocus
45179              * Fires when on first focus - needed by toolbars..
45180              * @param {HtmlEditor} this
45181              */
45182             firstfocus: true,
45183             /**
45184              * @event autosave
45185              * Auto save the htmlEditor value as a file into Events
45186              * @param {HtmlEditor} this
45187              */
45188             autosave: true,
45189             /**
45190              * @event savedpreview
45191              * preview the saved version of htmlEditor
45192              * @param {HtmlEditor} this
45193              */
45194             savedpreview: true,
45195             
45196             /**
45197             * @event stylesheetsclick
45198             * Fires when press the Sytlesheets button
45199             * @param {Roo.HtmlEditorCore} this
45200             */
45201             stylesheetsclick: true
45202         });
45203         this.defaultAutoCreate =  {
45204             tag: "textarea",
45205             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45206             autocomplete: "new-password"
45207         };
45208     },
45209
45210     /**
45211      * Protected method that will not generally be called directly. It
45212      * is called when the editor creates its toolbar. Override this method if you need to
45213      * add custom toolbar buttons.
45214      * @param {HtmlEditor} editor
45215      */
45216     createToolbar : function(editor){
45217         Roo.log("create toolbars");
45218         if (!editor.toolbars || !editor.toolbars.length) {
45219             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45220         }
45221         
45222         for (var i =0 ; i < editor.toolbars.length;i++) {
45223             editor.toolbars[i] = Roo.factory(
45224                     typeof(editor.toolbars[i]) == 'string' ?
45225                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45226                 Roo.form.HtmlEditor);
45227             editor.toolbars[i].init(editor);
45228         }
45229          
45230         
45231     },
45232
45233      
45234     // private
45235     onRender : function(ct, position)
45236     {
45237         var _t = this;
45238         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45239         
45240         this.wrap = this.el.wrap({
45241             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45242         });
45243         
45244         this.editorcore.onRender(ct, position);
45245          
45246         if (this.resizable) {
45247             this.resizeEl = new Roo.Resizable(this.wrap, {
45248                 pinned : true,
45249                 wrap: true,
45250                 dynamic : true,
45251                 minHeight : this.height,
45252                 height: this.height,
45253                 handles : this.resizable,
45254                 width: this.width,
45255                 listeners : {
45256                     resize : function(r, w, h) {
45257                         _t.onResize(w,h); // -something
45258                     }
45259                 }
45260             });
45261             
45262         }
45263         this.createToolbar(this);
45264        
45265         
45266         if(!this.width){
45267             this.setSize(this.wrap.getSize());
45268         }
45269         if (this.resizeEl) {
45270             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45271             // should trigger onReize..
45272         }
45273         
45274         this.keyNav = new Roo.KeyNav(this.el, {
45275             
45276             "tab" : function(e){
45277                 e.preventDefault();
45278                 
45279                 var value = this.getValue();
45280                 
45281                 var start = this.el.dom.selectionStart;
45282                 var end = this.el.dom.selectionEnd;
45283                 
45284                 if(!e.shiftKey){
45285                     
45286                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45287                     this.el.dom.setSelectionRange(end + 1, end + 1);
45288                     return;
45289                 }
45290                 
45291                 var f = value.substring(0, start).split("\t");
45292                 
45293                 if(f.pop().length != 0){
45294                     return;
45295                 }
45296                 
45297                 this.setValue(f.join("\t") + value.substring(end));
45298                 this.el.dom.setSelectionRange(start - 1, start - 1);
45299                 
45300             },
45301             
45302             "home" : function(e){
45303                 e.preventDefault();
45304                 
45305                 var curr = this.el.dom.selectionStart;
45306                 var lines = this.getValue().split("\n");
45307                 
45308                 if(!lines.length){
45309                     return;
45310                 }
45311                 
45312                 if(e.ctrlKey){
45313                     this.el.dom.setSelectionRange(0, 0);
45314                     return;
45315                 }
45316                 
45317                 var pos = 0;
45318                 
45319                 for (var i = 0; i < lines.length;i++) {
45320                     pos += lines[i].length;
45321                     
45322                     if(i != 0){
45323                         pos += 1;
45324                     }
45325                     
45326                     if(pos < curr){
45327                         continue;
45328                     }
45329                     
45330                     pos -= lines[i].length;
45331                     
45332                     break;
45333                 }
45334                 
45335                 if(!e.shiftKey){
45336                     this.el.dom.setSelectionRange(pos, pos);
45337                     return;
45338                 }
45339                 
45340                 this.el.dom.selectionStart = pos;
45341                 this.el.dom.selectionEnd = curr;
45342             },
45343             
45344             "end" : function(e){
45345                 e.preventDefault();
45346                 
45347                 var curr = this.el.dom.selectionStart;
45348                 var lines = this.getValue().split("\n");
45349                 
45350                 if(!lines.length){
45351                     return;
45352                 }
45353                 
45354                 if(e.ctrlKey){
45355                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45356                     return;
45357                 }
45358                 
45359                 var pos = 0;
45360                 
45361                 for (var i = 0; i < lines.length;i++) {
45362                     
45363                     pos += lines[i].length;
45364                     
45365                     if(i != 0){
45366                         pos += 1;
45367                     }
45368                     
45369                     if(pos < curr){
45370                         continue;
45371                     }
45372                     
45373                     break;
45374                 }
45375                 
45376                 if(!e.shiftKey){
45377                     this.el.dom.setSelectionRange(pos, pos);
45378                     return;
45379                 }
45380                 
45381                 this.el.dom.selectionStart = curr;
45382                 this.el.dom.selectionEnd = pos;
45383             },
45384
45385             scope : this,
45386
45387             doRelay : function(foo, bar, hname){
45388                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45389             },
45390
45391             forceKeyDown: true
45392         });
45393         
45394 //        if(this.autosave && this.w){
45395 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45396 //        }
45397     },
45398
45399     // private
45400     onResize : function(w, h)
45401     {
45402         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45403         var ew = false;
45404         var eh = false;
45405         
45406         if(this.el ){
45407             if(typeof w == 'number'){
45408                 var aw = w - this.wrap.getFrameWidth('lr');
45409                 this.el.setWidth(this.adjustWidth('textarea', aw));
45410                 ew = aw;
45411             }
45412             if(typeof h == 'number'){
45413                 var tbh = 0;
45414                 for (var i =0; i < this.toolbars.length;i++) {
45415                     // fixme - ask toolbars for heights?
45416                     tbh += this.toolbars[i].tb.el.getHeight();
45417                     if (this.toolbars[i].footer) {
45418                         tbh += this.toolbars[i].footer.el.getHeight();
45419                     }
45420                 }
45421                 
45422                 
45423                 
45424                 
45425                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45426                 ah -= 5; // knock a few pixes off for look..
45427 //                Roo.log(ah);
45428                 this.el.setHeight(this.adjustWidth('textarea', ah));
45429                 var eh = ah;
45430             }
45431         }
45432         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45433         this.editorcore.onResize(ew,eh);
45434         
45435     },
45436
45437     /**
45438      * Toggles the editor between standard and source edit mode.
45439      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45440      */
45441     toggleSourceEdit : function(sourceEditMode)
45442     {
45443         this.editorcore.toggleSourceEdit(sourceEditMode);
45444         
45445         if(this.editorcore.sourceEditMode){
45446             Roo.log('editor - showing textarea');
45447             
45448 //            Roo.log('in');
45449 //            Roo.log(this.syncValue());
45450             this.editorcore.syncValue();
45451             this.el.removeClass('x-hidden');
45452             this.el.dom.removeAttribute('tabIndex');
45453             this.el.focus();
45454             
45455             for (var i = 0; i < this.toolbars.length; i++) {
45456                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45457                     this.toolbars[i].tb.hide();
45458                     this.toolbars[i].footer.hide();
45459                 }
45460             }
45461             
45462         }else{
45463             Roo.log('editor - hiding textarea');
45464 //            Roo.log('out')
45465 //            Roo.log(this.pushValue()); 
45466             this.editorcore.pushValue();
45467             
45468             this.el.addClass('x-hidden');
45469             this.el.dom.setAttribute('tabIndex', -1);
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.show();
45474                     this.toolbars[i].footer.show();
45475                 }
45476             }
45477             
45478             //this.deferFocus();
45479         }
45480         
45481         this.setSize(this.wrap.getSize());
45482         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45483         
45484         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45485     },
45486  
45487     // private (for BoxComponent)
45488     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45489
45490     // private (for BoxComponent)
45491     getResizeEl : function(){
45492         return this.wrap;
45493     },
45494
45495     // private (for BoxComponent)
45496     getPositionEl : function(){
45497         return this.wrap;
45498     },
45499
45500     // private
45501     initEvents : function(){
45502         this.originalValue = this.getValue();
45503     },
45504
45505     /**
45506      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45507      * @method
45508      */
45509     markInvalid : Roo.emptyFn,
45510     /**
45511      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45512      * @method
45513      */
45514     clearInvalid : Roo.emptyFn,
45515
45516     setValue : function(v){
45517         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45518         this.editorcore.pushValue();
45519     },
45520
45521      
45522     // private
45523     deferFocus : function(){
45524         this.focus.defer(10, this);
45525     },
45526
45527     // doc'ed in Field
45528     focus : function(){
45529         this.editorcore.focus();
45530         
45531     },
45532       
45533
45534     // private
45535     onDestroy : function(){
45536         
45537         
45538         
45539         if(this.rendered){
45540             
45541             for (var i =0; i < this.toolbars.length;i++) {
45542                 // fixme - ask toolbars for heights?
45543                 this.toolbars[i].onDestroy();
45544             }
45545             
45546             this.wrap.dom.innerHTML = '';
45547             this.wrap.remove();
45548         }
45549     },
45550
45551     // private
45552     onFirstFocus : function(){
45553         //Roo.log("onFirstFocus");
45554         this.editorcore.onFirstFocus();
45555          for (var i =0; i < this.toolbars.length;i++) {
45556             this.toolbars[i].onFirstFocus();
45557         }
45558         
45559     },
45560     
45561     // private
45562     syncValue : function()
45563     {
45564         this.editorcore.syncValue();
45565     },
45566     
45567     pushValue : function()
45568     {
45569         this.editorcore.pushValue();
45570     },
45571     
45572     setStylesheets : function(stylesheets)
45573     {
45574         this.editorcore.setStylesheets(stylesheets);
45575     },
45576     
45577     removeStylesheets : function()
45578     {
45579         this.editorcore.removeStylesheets();
45580     }
45581      
45582     
45583     // hide stuff that is not compatible
45584     /**
45585      * @event blur
45586      * @hide
45587      */
45588     /**
45589      * @event change
45590      * @hide
45591      */
45592     /**
45593      * @event focus
45594      * @hide
45595      */
45596     /**
45597      * @event specialkey
45598      * @hide
45599      */
45600     /**
45601      * @cfg {String} fieldClass @hide
45602      */
45603     /**
45604      * @cfg {String} focusClass @hide
45605      */
45606     /**
45607      * @cfg {String} autoCreate @hide
45608      */
45609     /**
45610      * @cfg {String} inputType @hide
45611      */
45612     /**
45613      * @cfg {String} invalidClass @hide
45614      */
45615     /**
45616      * @cfg {String} invalidText @hide
45617      */
45618     /**
45619      * @cfg {String} msgFx @hide
45620      */
45621     /**
45622      * @cfg {String} validateOnBlur @hide
45623      */
45624 });
45625  
45626     // <script type="text/javascript">
45627 /*
45628  * Based on
45629  * Ext JS Library 1.1.1
45630  * Copyright(c) 2006-2007, Ext JS, LLC.
45631  *  
45632  
45633  */
45634
45635 /**
45636  * @class Roo.form.HtmlEditorToolbar1
45637  * Basic Toolbar
45638  * 
45639  * Usage:
45640  *
45641  new Roo.form.HtmlEditor({
45642     ....
45643     toolbars : [
45644         new Roo.form.HtmlEditorToolbar1({
45645             disable : { fonts: 1 , format: 1, ..., ... , ...],
45646             btns : [ .... ]
45647         })
45648     }
45649      
45650  * 
45651  * @cfg {Object} disable List of elements to disable..
45652  * @cfg {Array} btns List of additional buttons.
45653  * 
45654  * 
45655  * NEEDS Extra CSS? 
45656  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45657  */
45658  
45659 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45660 {
45661     
45662     Roo.apply(this, config);
45663     
45664     // default disabled, based on 'good practice'..
45665     this.disable = this.disable || {};
45666     Roo.applyIf(this.disable, {
45667         fontSize : true,
45668         colors : true,
45669         specialElements : true
45670     });
45671     
45672     
45673     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45674     // dont call parent... till later.
45675 }
45676
45677 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45678     
45679     tb: false,
45680     
45681     rendered: false,
45682     
45683     editor : false,
45684     editorcore : false,
45685     /**
45686      * @cfg {Object} disable  List of toolbar elements to disable
45687          
45688      */
45689     disable : false,
45690     
45691     
45692      /**
45693      * @cfg {String} createLinkText The default text for the create link prompt
45694      */
45695     createLinkText : 'Please enter the URL for the link:',
45696     /**
45697      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45698      */
45699     defaultLinkValue : 'http:/'+'/',
45700    
45701     
45702       /**
45703      * @cfg {Array} fontFamilies An array of available font families
45704      */
45705     fontFamilies : [
45706         'Arial',
45707         'Courier New',
45708         'Tahoma',
45709         'Times New Roman',
45710         'Verdana'
45711     ],
45712     
45713     specialChars : [
45714            "&#169;",
45715           "&#174;",     
45716           "&#8482;",    
45717           "&#163;" ,    
45718          // "&#8212;",    
45719           "&#8230;",    
45720           "&#247;" ,    
45721         //  "&#225;" ,     ?? a acute?
45722            "&#8364;"    , //Euro
45723        //   "&#8220;"    ,
45724         //  "&#8221;"    ,
45725         //  "&#8226;"    ,
45726           "&#176;"  //   , // degrees
45727
45728          // "&#233;"     , // e ecute
45729          // "&#250;"     , // u ecute?
45730     ],
45731     
45732     specialElements : [
45733         {
45734             text: "Insert Table",
45735             xtype: 'MenuItem',
45736             xns : Roo.Menu,
45737             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45738                 
45739         },
45740         {    
45741             text: "Insert Image",
45742             xtype: 'MenuItem',
45743             xns : Roo.Menu,
45744             ihtml : '<img src="about:blank"/>'
45745             
45746         }
45747         
45748          
45749     ],
45750     
45751     
45752     inputElements : [ 
45753             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45754             "input:submit", "input:button", "select", "textarea", "label" ],
45755     formats : [
45756         ["p"] ,  
45757         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45758         ["pre"],[ "code"], 
45759         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45760         ['div'],['span'],
45761         ['sup'],['sub']
45762     ],
45763     
45764     cleanStyles : [
45765         "font-size"
45766     ],
45767      /**
45768      * @cfg {String} defaultFont default font to use.
45769      */
45770     defaultFont: 'tahoma',
45771    
45772     fontSelect : false,
45773     
45774     
45775     formatCombo : false,
45776     
45777     init : function(editor)
45778     {
45779         this.editor = editor;
45780         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45781         var editorcore = this.editorcore;
45782         
45783         var _t = this;
45784         
45785         var fid = editorcore.frameId;
45786         var etb = this;
45787         function btn(id, toggle, handler){
45788             var xid = fid + '-'+ id ;
45789             return {
45790                 id : xid,
45791                 cmd : id,
45792                 cls : 'x-btn-icon x-edit-'+id,
45793                 enableToggle:toggle !== false,
45794                 scope: _t, // was editor...
45795                 handler:handler||_t.relayBtnCmd,
45796                 clickEvent:'mousedown',
45797                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45798                 tabIndex:-1
45799             };
45800         }
45801         
45802         
45803         
45804         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45805         this.tb = tb;
45806          // stop form submits
45807         tb.el.on('click', function(e){
45808             e.preventDefault(); // what does this do?
45809         });
45810
45811         if(!this.disable.font) { // && !Roo.isSafari){
45812             /* why no safari for fonts 
45813             editor.fontSelect = tb.el.createChild({
45814                 tag:'select',
45815                 tabIndex: -1,
45816                 cls:'x-font-select',
45817                 html: this.createFontOptions()
45818             });
45819             
45820             editor.fontSelect.on('change', function(){
45821                 var font = editor.fontSelect.dom.value;
45822                 editor.relayCmd('fontname', font);
45823                 editor.deferFocus();
45824             }, editor);
45825             
45826             tb.add(
45827                 editor.fontSelect.dom,
45828                 '-'
45829             );
45830             */
45831             
45832         };
45833         if(!this.disable.formats){
45834             this.formatCombo = new Roo.form.ComboBox({
45835                 store: new Roo.data.SimpleStore({
45836                     id : 'tag',
45837                     fields: ['tag'],
45838                     data : this.formats // from states.js
45839                 }),
45840                 blockFocus : true,
45841                 name : '',
45842                 //autoCreate : {tag: "div",  size: "20"},
45843                 displayField:'tag',
45844                 typeAhead: false,
45845                 mode: 'local',
45846                 editable : false,
45847                 triggerAction: 'all',
45848                 emptyText:'Add tag',
45849                 selectOnFocus:true,
45850                 width:135,
45851                 listeners : {
45852                     'select': function(c, r, i) {
45853                         editorcore.insertTag(r.get('tag'));
45854                         editor.focus();
45855                     }
45856                 }
45857
45858             });
45859             tb.addField(this.formatCombo);
45860             
45861         }
45862         
45863         if(!this.disable.format){
45864             tb.add(
45865                 btn('bold'),
45866                 btn('italic'),
45867                 btn('underline'),
45868                 btn('strikethrough')
45869             );
45870         };
45871         if(!this.disable.fontSize){
45872             tb.add(
45873                 '-',
45874                 
45875                 
45876                 btn('increasefontsize', false, editorcore.adjustFont),
45877                 btn('decreasefontsize', false, editorcore.adjustFont)
45878             );
45879         };
45880         
45881         
45882         if(!this.disable.colors){
45883             tb.add(
45884                 '-', {
45885                     id:editorcore.frameId +'-forecolor',
45886                     cls:'x-btn-icon x-edit-forecolor',
45887                     clickEvent:'mousedown',
45888                     tooltip: this.buttonTips['forecolor'] || undefined,
45889                     tabIndex:-1,
45890                     menu : new Roo.menu.ColorMenu({
45891                         allowReselect: true,
45892                         focus: Roo.emptyFn,
45893                         value:'000000',
45894                         plain:true,
45895                         selectHandler: function(cp, color){
45896                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45897                             editor.deferFocus();
45898                         },
45899                         scope: editorcore,
45900                         clickEvent:'mousedown'
45901                     })
45902                 }, {
45903                     id:editorcore.frameId +'backcolor',
45904                     cls:'x-btn-icon x-edit-backcolor',
45905                     clickEvent:'mousedown',
45906                     tooltip: this.buttonTips['backcolor'] || undefined,
45907                     tabIndex:-1,
45908                     menu : new Roo.menu.ColorMenu({
45909                         focus: Roo.emptyFn,
45910                         value:'FFFFFF',
45911                         plain:true,
45912                         allowReselect: true,
45913                         selectHandler: function(cp, color){
45914                             if(Roo.isGecko){
45915                                 editorcore.execCmd('useCSS', false);
45916                                 editorcore.execCmd('hilitecolor', color);
45917                                 editorcore.execCmd('useCSS', true);
45918                                 editor.deferFocus();
45919                             }else{
45920                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45921                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45922                                 editor.deferFocus();
45923                             }
45924                         },
45925                         scope:editorcore,
45926                         clickEvent:'mousedown'
45927                     })
45928                 }
45929             );
45930         };
45931         // now add all the items...
45932         
45933
45934         if(!this.disable.alignments){
45935             tb.add(
45936                 '-',
45937                 btn('justifyleft'),
45938                 btn('justifycenter'),
45939                 btn('justifyright')
45940             );
45941         };
45942
45943         //if(!Roo.isSafari){
45944             if(!this.disable.links){
45945                 tb.add(
45946                     '-',
45947                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45948                 );
45949             };
45950
45951             if(!this.disable.lists){
45952                 tb.add(
45953                     '-',
45954                     btn('insertorderedlist'),
45955                     btn('insertunorderedlist')
45956                 );
45957             }
45958             if(!this.disable.sourceEdit){
45959                 tb.add(
45960                     '-',
45961                     btn('sourceedit', true, function(btn){
45962                         this.toggleSourceEdit(btn.pressed);
45963                     })
45964                 );
45965             }
45966         //}
45967         
45968         var smenu = { };
45969         // special menu.. - needs to be tidied up..
45970         if (!this.disable.special) {
45971             smenu = {
45972                 text: "&#169;",
45973                 cls: 'x-edit-none',
45974                 
45975                 menu : {
45976                     items : []
45977                 }
45978             };
45979             for (var i =0; i < this.specialChars.length; i++) {
45980                 smenu.menu.items.push({
45981                     
45982                     html: this.specialChars[i],
45983                     handler: function(a,b) {
45984                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45985                         //editor.insertAtCursor(a.html);
45986                         
45987                     },
45988                     tabIndex:-1
45989                 });
45990             }
45991             
45992             
45993             tb.add(smenu);
45994             
45995             
45996         }
45997         
45998         var cmenu = { };
45999         if (!this.disable.cleanStyles) {
46000             cmenu = {
46001                 cls: 'x-btn-icon x-btn-clear',
46002                 
46003                 menu : {
46004                     items : []
46005                 }
46006             };
46007             for (var i =0; i < this.cleanStyles.length; i++) {
46008                 cmenu.menu.items.push({
46009                     actiontype : this.cleanStyles[i],
46010                     html: 'Remove ' + this.cleanStyles[i],
46011                     handler: function(a,b) {
46012 //                        Roo.log(a);
46013 //                        Roo.log(b);
46014                         var c = Roo.get(editorcore.doc.body);
46015                         c.select('[style]').each(function(s) {
46016                             s.dom.style.removeProperty(a.actiontype);
46017                         });
46018                         editorcore.syncValue();
46019                     },
46020                     tabIndex:-1
46021                 });
46022             }
46023              cmenu.menu.items.push({
46024                 actiontype : 'tablewidths',
46025                 html: 'Remove Table Widths',
46026                 handler: function(a,b) {
46027                     editorcore.cleanTableWidths();
46028                     editorcore.syncValue();
46029                 },
46030                 tabIndex:-1
46031             });
46032             cmenu.menu.items.push({
46033                 actiontype : 'word',
46034                 html: 'Remove MS Word Formating',
46035                 handler: function(a,b) {
46036                     editorcore.cleanWord();
46037                     editorcore.syncValue();
46038                 },
46039                 tabIndex:-1
46040             });
46041             
46042             cmenu.menu.items.push({
46043                 actiontype : 'all',
46044                 html: 'Remove All Styles',
46045                 handler: function(a,b) {
46046                     
46047                     var c = Roo.get(editorcore.doc.body);
46048                     c.select('[style]').each(function(s) {
46049                         s.dom.removeAttribute('style');
46050                     });
46051                     editorcore.syncValue();
46052                 },
46053                 tabIndex:-1
46054             });
46055             
46056             cmenu.menu.items.push({
46057                 actiontype : 'all',
46058                 html: 'Remove All CSS Classes',
46059                 handler: function(a,b) {
46060                     
46061                     var c = Roo.get(editorcore.doc.body);
46062                     c.select('[class]').each(function(s) {
46063                         s.dom.removeAttribute('class');
46064                     });
46065                     editorcore.cleanWord();
46066                     editorcore.syncValue();
46067                 },
46068                 tabIndex:-1
46069             });
46070             
46071              cmenu.menu.items.push({
46072                 actiontype : 'tidy',
46073                 html: 'Tidy HTML Source',
46074                 handler: function(a,b) {
46075                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46076                     editorcore.syncValue();
46077                 },
46078                 tabIndex:-1
46079             });
46080             
46081             
46082             tb.add(cmenu);
46083         }
46084          
46085         if (!this.disable.specialElements) {
46086             var semenu = {
46087                 text: "Other;",
46088                 cls: 'x-edit-none',
46089                 menu : {
46090                     items : []
46091                 }
46092             };
46093             for (var i =0; i < this.specialElements.length; i++) {
46094                 semenu.menu.items.push(
46095                     Roo.apply({ 
46096                         handler: function(a,b) {
46097                             editor.insertAtCursor(this.ihtml);
46098                         }
46099                     }, this.specialElements[i])
46100                 );
46101                     
46102             }
46103             
46104             tb.add(semenu);
46105             
46106             
46107         }
46108          
46109         
46110         if (this.btns) {
46111             for(var i =0; i< this.btns.length;i++) {
46112                 var b = Roo.factory(this.btns[i],Roo.form);
46113                 b.cls =  'x-edit-none';
46114                 
46115                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46116                     b.cls += ' x-init-enable';
46117                 }
46118                 
46119                 b.scope = editorcore;
46120                 tb.add(b);
46121             }
46122         
46123         }
46124         
46125         
46126         
46127         // disable everything...
46128         
46129         this.tb.items.each(function(item){
46130             
46131            if(
46132                 item.id != editorcore.frameId+ '-sourceedit' && 
46133                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46134             ){
46135                 
46136                 item.disable();
46137             }
46138         });
46139         this.rendered = true;
46140         
46141         // the all the btns;
46142         editor.on('editorevent', this.updateToolbar, this);
46143         // other toolbars need to implement this..
46144         //editor.on('editmodechange', this.updateToolbar, this);
46145     },
46146     
46147     
46148     relayBtnCmd : function(btn) {
46149         this.editorcore.relayCmd(btn.cmd);
46150     },
46151     // private used internally
46152     createLink : function(){
46153         Roo.log("create link?");
46154         var url = prompt(this.createLinkText, this.defaultLinkValue);
46155         if(url && url != 'http:/'+'/'){
46156             this.editorcore.relayCmd('createlink', url);
46157         }
46158     },
46159
46160     
46161     /**
46162      * Protected method that will not generally be called directly. It triggers
46163      * a toolbar update by reading the markup state of the current selection in the editor.
46164      */
46165     updateToolbar: function(){
46166
46167         if(!this.editorcore.activated){
46168             this.editor.onFirstFocus();
46169             return;
46170         }
46171
46172         var btns = this.tb.items.map, 
46173             doc = this.editorcore.doc,
46174             frameId = this.editorcore.frameId;
46175
46176         if(!this.disable.font && !Roo.isSafari){
46177             /*
46178             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46179             if(name != this.fontSelect.dom.value){
46180                 this.fontSelect.dom.value = name;
46181             }
46182             */
46183         }
46184         if(!this.disable.format){
46185             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46186             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46187             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46188             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46189         }
46190         if(!this.disable.alignments){
46191             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46192             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46193             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46194         }
46195         if(!Roo.isSafari && !this.disable.lists){
46196             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46197             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46198         }
46199         
46200         var ans = this.editorcore.getAllAncestors();
46201         if (this.formatCombo) {
46202             
46203             
46204             var store = this.formatCombo.store;
46205             this.formatCombo.setValue("");
46206             for (var i =0; i < ans.length;i++) {
46207                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46208                     // select it..
46209                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46210                     break;
46211                 }
46212             }
46213         }
46214         
46215         
46216         
46217         // hides menus... - so this cant be on a menu...
46218         Roo.menu.MenuMgr.hideAll();
46219
46220         //this.editorsyncValue();
46221     },
46222    
46223     
46224     createFontOptions : function(){
46225         var buf = [], fs = this.fontFamilies, ff, lc;
46226         
46227         
46228         
46229         for(var i = 0, len = fs.length; i< len; i++){
46230             ff = fs[i];
46231             lc = ff.toLowerCase();
46232             buf.push(
46233                 '<option value="',lc,'" style="font-family:',ff,';"',
46234                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46235                     ff,
46236                 '</option>'
46237             );
46238         }
46239         return buf.join('');
46240     },
46241     
46242     toggleSourceEdit : function(sourceEditMode){
46243         
46244         Roo.log("toolbar toogle");
46245         if(sourceEditMode === undefined){
46246             sourceEditMode = !this.sourceEditMode;
46247         }
46248         this.sourceEditMode = sourceEditMode === true;
46249         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46250         // just toggle the button?
46251         if(btn.pressed !== this.sourceEditMode){
46252             btn.toggle(this.sourceEditMode);
46253             return;
46254         }
46255         
46256         if(sourceEditMode){
46257             Roo.log("disabling buttons");
46258             this.tb.items.each(function(item){
46259                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46260                     item.disable();
46261                 }
46262             });
46263           
46264         }else{
46265             Roo.log("enabling buttons");
46266             if(this.editorcore.initialized){
46267                 this.tb.items.each(function(item){
46268                     item.enable();
46269                 });
46270             }
46271             
46272         }
46273         Roo.log("calling toggole on editor");
46274         // tell the editor that it's been pressed..
46275         this.editor.toggleSourceEdit(sourceEditMode);
46276        
46277     },
46278      /**
46279      * Object collection of toolbar tooltips for the buttons in the editor. The key
46280      * is the command id associated with that button and the value is a valid QuickTips object.
46281      * For example:
46282 <pre><code>
46283 {
46284     bold : {
46285         title: 'Bold (Ctrl+B)',
46286         text: 'Make the selected text bold.',
46287         cls: 'x-html-editor-tip'
46288     },
46289     italic : {
46290         title: 'Italic (Ctrl+I)',
46291         text: 'Make the selected text italic.',
46292         cls: 'x-html-editor-tip'
46293     },
46294     ...
46295 </code></pre>
46296     * @type Object
46297      */
46298     buttonTips : {
46299         bold : {
46300             title: 'Bold (Ctrl+B)',
46301             text: 'Make the selected text bold.',
46302             cls: 'x-html-editor-tip'
46303         },
46304         italic : {
46305             title: 'Italic (Ctrl+I)',
46306             text: 'Make the selected text italic.',
46307             cls: 'x-html-editor-tip'
46308         },
46309         underline : {
46310             title: 'Underline (Ctrl+U)',
46311             text: 'Underline the selected text.',
46312             cls: 'x-html-editor-tip'
46313         },
46314         strikethrough : {
46315             title: 'Strikethrough',
46316             text: 'Strikethrough the selected text.',
46317             cls: 'x-html-editor-tip'
46318         },
46319         increasefontsize : {
46320             title: 'Grow Text',
46321             text: 'Increase the font size.',
46322             cls: 'x-html-editor-tip'
46323         },
46324         decreasefontsize : {
46325             title: 'Shrink Text',
46326             text: 'Decrease the font size.',
46327             cls: 'x-html-editor-tip'
46328         },
46329         backcolor : {
46330             title: 'Text Highlight Color',
46331             text: 'Change the background color of the selected text.',
46332             cls: 'x-html-editor-tip'
46333         },
46334         forecolor : {
46335             title: 'Font Color',
46336             text: 'Change the color of the selected text.',
46337             cls: 'x-html-editor-tip'
46338         },
46339         justifyleft : {
46340             title: 'Align Text Left',
46341             text: 'Align text to the left.',
46342             cls: 'x-html-editor-tip'
46343         },
46344         justifycenter : {
46345             title: 'Center Text',
46346             text: 'Center text in the editor.',
46347             cls: 'x-html-editor-tip'
46348         },
46349         justifyright : {
46350             title: 'Align Text Right',
46351             text: 'Align text to the right.',
46352             cls: 'x-html-editor-tip'
46353         },
46354         insertunorderedlist : {
46355             title: 'Bullet List',
46356             text: 'Start a bulleted list.',
46357             cls: 'x-html-editor-tip'
46358         },
46359         insertorderedlist : {
46360             title: 'Numbered List',
46361             text: 'Start a numbered list.',
46362             cls: 'x-html-editor-tip'
46363         },
46364         createlink : {
46365             title: 'Hyperlink',
46366             text: 'Make the selected text a hyperlink.',
46367             cls: 'x-html-editor-tip'
46368         },
46369         sourceedit : {
46370             title: 'Source Edit',
46371             text: 'Switch to source editing mode.',
46372             cls: 'x-html-editor-tip'
46373         }
46374     },
46375     // private
46376     onDestroy : function(){
46377         if(this.rendered){
46378             
46379             this.tb.items.each(function(item){
46380                 if(item.menu){
46381                     item.menu.removeAll();
46382                     if(item.menu.el){
46383                         item.menu.el.destroy();
46384                     }
46385                 }
46386                 item.destroy();
46387             });
46388              
46389         }
46390     },
46391     onFirstFocus: function() {
46392         this.tb.items.each(function(item){
46393            item.enable();
46394         });
46395     }
46396 });
46397
46398
46399
46400
46401 // <script type="text/javascript">
46402 /*
46403  * Based on
46404  * Ext JS Library 1.1.1
46405  * Copyright(c) 2006-2007, Ext JS, LLC.
46406  *  
46407  
46408  */
46409
46410  
46411 /**
46412  * @class Roo.form.HtmlEditor.ToolbarContext
46413  * Context Toolbar
46414  * 
46415  * Usage:
46416  *
46417  new Roo.form.HtmlEditor({
46418     ....
46419     toolbars : [
46420         { xtype: 'ToolbarStandard', styles : {} }
46421         { xtype: 'ToolbarContext', disable : {} }
46422     ]
46423 })
46424
46425      
46426  * 
46427  * @config : {Object} disable List of elements to disable.. (not done yet.)
46428  * @config : {Object} styles  Map of styles available.
46429  * 
46430  */
46431
46432 Roo.form.HtmlEditor.ToolbarContext = function(config)
46433 {
46434     
46435     Roo.apply(this, config);
46436     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46437     // dont call parent... till later.
46438     this.styles = this.styles || {};
46439 }
46440
46441  
46442
46443 Roo.form.HtmlEditor.ToolbarContext.types = {
46444     'IMG' : {
46445         width : {
46446             title: "Width",
46447             width: 40
46448         },
46449         height:  {
46450             title: "Height",
46451             width: 40
46452         },
46453         align: {
46454             title: "Align",
46455             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46456             width : 80
46457             
46458         },
46459         border: {
46460             title: "Border",
46461             width: 40
46462         },
46463         alt: {
46464             title: "Alt",
46465             width: 120
46466         },
46467         src : {
46468             title: "Src",
46469             width: 220
46470         }
46471         
46472     },
46473     'A' : {
46474         name : {
46475             title: "Name",
46476             width: 50
46477         },
46478         target:  {
46479             title: "Target",
46480             width: 120
46481         },
46482         href:  {
46483             title: "Href",
46484             width: 220
46485         } // border?
46486         
46487     },
46488     'TABLE' : {
46489         rows : {
46490             title: "Rows",
46491             width: 20
46492         },
46493         cols : {
46494             title: "Cols",
46495             width: 20
46496         },
46497         width : {
46498             title: "Width",
46499             width: 40
46500         },
46501         height : {
46502             title: "Height",
46503             width: 40
46504         },
46505         border : {
46506             title: "Border",
46507             width: 20
46508         }
46509     },
46510     'TD' : {
46511         width : {
46512             title: "Width",
46513             width: 40
46514         },
46515         height : {
46516             title: "Height",
46517             width: 40
46518         },   
46519         align: {
46520             title: "Align",
46521             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46522             width: 80
46523         },
46524         valign: {
46525             title: "Valign",
46526             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46527             width: 80
46528         },
46529         colspan: {
46530             title: "Colspan",
46531             width: 20
46532             
46533         },
46534          'font-family'  : {
46535             title : "Font",
46536             style : 'fontFamily',
46537             displayField: 'display',
46538             optname : 'font-family',
46539             width: 140
46540         }
46541     },
46542     'INPUT' : {
46543         name : {
46544             title: "name",
46545             width: 120
46546         },
46547         value : {
46548             title: "Value",
46549             width: 120
46550         },
46551         width : {
46552             title: "Width",
46553             width: 40
46554         }
46555     },
46556     'LABEL' : {
46557         'for' : {
46558             title: "For",
46559             width: 120
46560         }
46561     },
46562     'TEXTAREA' : {
46563           name : {
46564             title: "name",
46565             width: 120
46566         },
46567         rows : {
46568             title: "Rows",
46569             width: 20
46570         },
46571         cols : {
46572             title: "Cols",
46573             width: 20
46574         }
46575     },
46576     'SELECT' : {
46577         name : {
46578             title: "name",
46579             width: 120
46580         },
46581         selectoptions : {
46582             title: "Options",
46583             width: 200
46584         }
46585     },
46586     
46587     // should we really allow this??
46588     // should this just be 
46589     'BODY' : {
46590         title : {
46591             title: "Title",
46592             width: 200,
46593             disabled : true
46594         }
46595     },
46596     'SPAN' : {
46597         'font-family'  : {
46598             title : "Font",
46599             style : 'fontFamily',
46600             displayField: 'display',
46601             optname : 'font-family',
46602             width: 140
46603         }
46604     },
46605     'DIV' : {
46606         'font-family'  : {
46607             title : "Font",
46608             style : 'fontFamily',
46609             displayField: 'display',
46610             optname : 'font-family',
46611             width: 140
46612         }
46613     },
46614      'P' : {
46615         'font-family'  : {
46616             title : "Font",
46617             style : 'fontFamily',
46618             displayField: 'display',
46619             optname : 'font-family',
46620             width: 140
46621         }
46622     },
46623     
46624     '*' : {
46625         // empty..
46626     }
46627
46628 };
46629
46630 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46631 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46632
46633 Roo.form.HtmlEditor.ToolbarContext.options = {
46634         'font-family'  : [ 
46635                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46636                 [ 'Courier New', 'Courier New'],
46637                 [ 'Tahoma', 'Tahoma'],
46638                 [ 'Times New Roman,serif', 'Times'],
46639                 [ 'Verdana','Verdana' ]
46640         ]
46641 };
46642
46643 // fixme - these need to be configurable..
46644  
46645
46646 //Roo.form.HtmlEditor.ToolbarContext.types
46647
46648
46649 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46650     
46651     tb: false,
46652     
46653     rendered: false,
46654     
46655     editor : false,
46656     editorcore : false,
46657     /**
46658      * @cfg {Object} disable  List of toolbar elements to disable
46659          
46660      */
46661     disable : false,
46662     /**
46663      * @cfg {Object} styles List of styles 
46664      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46665      *
46666      * These must be defined in the page, so they get rendered correctly..
46667      * .headline { }
46668      * TD.underline { }
46669      * 
46670      */
46671     styles : false,
46672     
46673     options: false,
46674     
46675     toolbars : false,
46676     
46677     init : function(editor)
46678     {
46679         this.editor = editor;
46680         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46681         var editorcore = this.editorcore;
46682         
46683         var fid = editorcore.frameId;
46684         var etb = this;
46685         function btn(id, toggle, handler){
46686             var xid = fid + '-'+ id ;
46687             return {
46688                 id : xid,
46689                 cmd : id,
46690                 cls : 'x-btn-icon x-edit-'+id,
46691                 enableToggle:toggle !== false,
46692                 scope: editorcore, // was editor...
46693                 handler:handler||editorcore.relayBtnCmd,
46694                 clickEvent:'mousedown',
46695                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46696                 tabIndex:-1
46697             };
46698         }
46699         // create a new element.
46700         var wdiv = editor.wrap.createChild({
46701                 tag: 'div'
46702             }, editor.wrap.dom.firstChild.nextSibling, true);
46703         
46704         // can we do this more than once??
46705         
46706          // stop form submits
46707       
46708  
46709         // disable everything...
46710         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46711         this.toolbars = {};
46712            
46713         for (var i in  ty) {
46714           
46715             this.toolbars[i] = this.buildToolbar(ty[i],i);
46716         }
46717         this.tb = this.toolbars.BODY;
46718         this.tb.el.show();
46719         this.buildFooter();
46720         this.footer.show();
46721         editor.on('hide', function( ) { this.footer.hide() }, this);
46722         editor.on('show', function( ) { this.footer.show() }, this);
46723         
46724          
46725         this.rendered = true;
46726         
46727         // the all the btns;
46728         editor.on('editorevent', this.updateToolbar, this);
46729         // other toolbars need to implement this..
46730         //editor.on('editmodechange', this.updateToolbar, this);
46731     },
46732     
46733     
46734     
46735     /**
46736      * Protected method that will not generally be called directly. It triggers
46737      * a toolbar update by reading the markup state of the current selection in the editor.
46738      *
46739      * Note you can force an update by calling on('editorevent', scope, false)
46740      */
46741     updateToolbar: function(editor,ev,sel){
46742
46743         //Roo.log(ev);
46744         // capture mouse up - this is handy for selecting images..
46745         // perhaps should go somewhere else...
46746         if(!this.editorcore.activated){
46747              this.editor.onFirstFocus();
46748             return;
46749         }
46750         
46751         
46752         
46753         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46754         // selectNode - might want to handle IE?
46755         if (ev &&
46756             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46757             ev.target && ev.target.tagName == 'IMG') {
46758             // they have click on an image...
46759             // let's see if we can change the selection...
46760             sel = ev.target;
46761          
46762               var nodeRange = sel.ownerDocument.createRange();
46763             try {
46764                 nodeRange.selectNode(sel);
46765             } catch (e) {
46766                 nodeRange.selectNodeContents(sel);
46767             }
46768             //nodeRange.collapse(true);
46769             var s = this.editorcore.win.getSelection();
46770             s.removeAllRanges();
46771             s.addRange(nodeRange);
46772         }  
46773         
46774       
46775         var updateFooter = sel ? false : true;
46776         
46777         
46778         var ans = this.editorcore.getAllAncestors();
46779         
46780         // pick
46781         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46782         
46783         if (!sel) { 
46784             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46785             sel = sel ? sel : this.editorcore.doc.body;
46786             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46787             
46788         }
46789         // pick a menu that exists..
46790         var tn = sel.tagName.toUpperCase();
46791         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46792         
46793         tn = sel.tagName.toUpperCase();
46794         
46795         var lastSel = this.tb.selectedNode;
46796         
46797         this.tb.selectedNode = sel;
46798         
46799         // if current menu does not match..
46800         
46801         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46802                 
46803             this.tb.el.hide();
46804             ///console.log("show: " + tn);
46805             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46806             this.tb.el.show();
46807             // update name
46808             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46809             
46810             
46811             // update attributes
46812             if (this.tb.fields) {
46813                 this.tb.fields.each(function(e) {
46814                     if (e.stylename) {
46815                         e.setValue(sel.style[e.stylename]);
46816                         return;
46817                     } 
46818                    e.setValue(sel.getAttribute(e.attrname));
46819                 });
46820             }
46821             
46822             var hasStyles = false;
46823             for(var i in this.styles) {
46824                 hasStyles = true;
46825                 break;
46826             }
46827             
46828             // update styles
46829             if (hasStyles) { 
46830                 var st = this.tb.fields.item(0);
46831                 
46832                 st.store.removeAll();
46833                
46834                 
46835                 var cn = sel.className.split(/\s+/);
46836                 
46837                 var avs = [];
46838                 if (this.styles['*']) {
46839                     
46840                     Roo.each(this.styles['*'], function(v) {
46841                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46842                     });
46843                 }
46844                 if (this.styles[tn]) { 
46845                     Roo.each(this.styles[tn], function(v) {
46846                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46847                     });
46848                 }
46849                 
46850                 st.store.loadData(avs);
46851                 st.collapse();
46852                 st.setValue(cn);
46853             }
46854             // flag our selected Node.
46855             this.tb.selectedNode = sel;
46856            
46857            
46858             Roo.menu.MenuMgr.hideAll();
46859
46860         }
46861         
46862         if (!updateFooter) {
46863             //this.footDisp.dom.innerHTML = ''; 
46864             return;
46865         }
46866         // update the footer
46867         //
46868         var html = '';
46869         
46870         this.footerEls = ans.reverse();
46871         Roo.each(this.footerEls, function(a,i) {
46872             if (!a) { return; }
46873             html += html.length ? ' &gt; '  :  '';
46874             
46875             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46876             
46877         });
46878        
46879         // 
46880         var sz = this.footDisp.up('td').getSize();
46881         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46882         this.footDisp.dom.style.marginLeft = '5px';
46883         
46884         this.footDisp.dom.style.overflow = 'hidden';
46885         
46886         this.footDisp.dom.innerHTML = html;
46887             
46888         //this.editorsyncValue();
46889     },
46890      
46891     
46892    
46893        
46894     // private
46895     onDestroy : function(){
46896         if(this.rendered){
46897             
46898             this.tb.items.each(function(item){
46899                 if(item.menu){
46900                     item.menu.removeAll();
46901                     if(item.menu.el){
46902                         item.menu.el.destroy();
46903                     }
46904                 }
46905                 item.destroy();
46906             });
46907              
46908         }
46909     },
46910     onFirstFocus: function() {
46911         // need to do this for all the toolbars..
46912         this.tb.items.each(function(item){
46913            item.enable();
46914         });
46915     },
46916     buildToolbar: function(tlist, nm)
46917     {
46918         var editor = this.editor;
46919         var editorcore = this.editorcore;
46920          // create a new element.
46921         var wdiv = editor.wrap.createChild({
46922                 tag: 'div'
46923             }, editor.wrap.dom.firstChild.nextSibling, true);
46924         
46925        
46926         var tb = new Roo.Toolbar(wdiv);
46927         // add the name..
46928         
46929         tb.add(nm+ ":&nbsp;");
46930         
46931         var styles = [];
46932         for(var i in this.styles) {
46933             styles.push(i);
46934         }
46935         
46936         // styles...
46937         if (styles && styles.length) {
46938             
46939             // this needs a multi-select checkbox...
46940             tb.addField( new Roo.form.ComboBox({
46941                 store: new Roo.data.SimpleStore({
46942                     id : 'val',
46943                     fields: ['val', 'selected'],
46944                     data : [] 
46945                 }),
46946                 name : '-roo-edit-className',
46947                 attrname : 'className',
46948                 displayField: 'val',
46949                 typeAhead: false,
46950                 mode: 'local',
46951                 editable : false,
46952                 triggerAction: 'all',
46953                 emptyText:'Select Style',
46954                 selectOnFocus:true,
46955                 width: 130,
46956                 listeners : {
46957                     'select': function(c, r, i) {
46958                         // initial support only for on class per el..
46959                         tb.selectedNode.className =  r ? r.get('val') : '';
46960                         editorcore.syncValue();
46961                     }
46962                 }
46963     
46964             }));
46965         }
46966         
46967         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46968         var tbops = tbc.options;
46969         
46970         for (var i in tlist) {
46971             
46972             var item = tlist[i];
46973             tb.add(item.title + ":&nbsp;");
46974             
46975             
46976             //optname == used so you can configure the options available..
46977             var opts = item.opts ? item.opts : false;
46978             if (item.optname) {
46979                 opts = tbops[item.optname];
46980            
46981             }
46982             
46983             if (opts) {
46984                 // opts == pulldown..
46985                 tb.addField( new Roo.form.ComboBox({
46986                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46987                         id : 'val',
46988                         fields: ['val', 'display'],
46989                         data : opts  
46990                     }),
46991                     name : '-roo-edit-' + i,
46992                     attrname : i,
46993                     stylename : item.style ? item.style : false,
46994                     displayField: item.displayField ? item.displayField : 'val',
46995                     valueField :  'val',
46996                     typeAhead: false,
46997                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46998                     editable : false,
46999                     triggerAction: 'all',
47000                     emptyText:'Select',
47001                     selectOnFocus:true,
47002                     width: item.width ? item.width  : 130,
47003                     listeners : {
47004                         'select': function(c, r, i) {
47005                             if (c.stylename) {
47006                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47007                                 return;
47008                             }
47009                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47010                         }
47011                     }
47012
47013                 }));
47014                 continue;
47015                     
47016                  
47017                 
47018                 tb.addField( new Roo.form.TextField({
47019                     name: i,
47020                     width: 100,
47021                     //allowBlank:false,
47022                     value: ''
47023                 }));
47024                 continue;
47025             }
47026             tb.addField( new Roo.form.TextField({
47027                 name: '-roo-edit-' + i,
47028                 attrname : i,
47029                 
47030                 width: item.width,
47031                 //allowBlank:true,
47032                 value: '',
47033                 listeners: {
47034                     'change' : function(f, nv, ov) {
47035                         tb.selectedNode.setAttribute(f.attrname, nv);
47036                         editorcore.syncValue();
47037                     }
47038                 }
47039             }));
47040              
47041         }
47042         
47043         var _this = this;
47044         
47045         if(nm == 'BODY'){
47046             tb.addSeparator();
47047         
47048             tb.addButton( {
47049                 text: 'Stylesheets',
47050
47051                 listeners : {
47052                     click : function ()
47053                     {
47054                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47055                     }
47056                 }
47057             });
47058         }
47059         
47060         tb.addFill();
47061         tb.addButton( {
47062             text: 'Remove Tag',
47063     
47064             listeners : {
47065                 click : function ()
47066                 {
47067                     // remove
47068                     // undo does not work.
47069                      
47070                     var sn = tb.selectedNode;
47071                     
47072                     var pn = sn.parentNode;
47073                     
47074                     var stn =  sn.childNodes[0];
47075                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47076                     while (sn.childNodes.length) {
47077                         var node = sn.childNodes[0];
47078                         sn.removeChild(node);
47079                         //Roo.log(node);
47080                         pn.insertBefore(node, sn);
47081                         
47082                     }
47083                     pn.removeChild(sn);
47084                     var range = editorcore.createRange();
47085         
47086                     range.setStart(stn,0);
47087                     range.setEnd(en,0); //????
47088                     //range.selectNode(sel);
47089                     
47090                     
47091                     var selection = editorcore.getSelection();
47092                     selection.removeAllRanges();
47093                     selection.addRange(range);
47094                     
47095                     
47096                     
47097                     //_this.updateToolbar(null, null, pn);
47098                     _this.updateToolbar(null, null, null);
47099                     _this.footDisp.dom.innerHTML = ''; 
47100                 }
47101             }
47102             
47103                     
47104                 
47105             
47106         });
47107         
47108         
47109         tb.el.on('click', function(e){
47110             e.preventDefault(); // what does this do?
47111         });
47112         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47113         tb.el.hide();
47114         tb.name = nm;
47115         // dont need to disable them... as they will get hidden
47116         return tb;
47117          
47118         
47119     },
47120     buildFooter : function()
47121     {
47122         
47123         var fel = this.editor.wrap.createChild();
47124         this.footer = new Roo.Toolbar(fel);
47125         // toolbar has scrolly on left / right?
47126         var footDisp= new Roo.Toolbar.Fill();
47127         var _t = this;
47128         this.footer.add(
47129             {
47130                 text : '&lt;',
47131                 xtype: 'Button',
47132                 handler : function() {
47133                     _t.footDisp.scrollTo('left',0,true)
47134                 }
47135             }
47136         );
47137         this.footer.add( footDisp );
47138         this.footer.add( 
47139             {
47140                 text : '&gt;',
47141                 xtype: 'Button',
47142                 handler : function() {
47143                     // no animation..
47144                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47145                 }
47146             }
47147         );
47148         var fel = Roo.get(footDisp.el);
47149         fel.addClass('x-editor-context');
47150         this.footDispWrap = fel; 
47151         this.footDispWrap.overflow  = 'hidden';
47152         
47153         this.footDisp = fel.createChild();
47154         this.footDispWrap.on('click', this.onContextClick, this)
47155         
47156         
47157     },
47158     onContextClick : function (ev,dom)
47159     {
47160         ev.preventDefault();
47161         var  cn = dom.className;
47162         //Roo.log(cn);
47163         if (!cn.match(/x-ed-loc-/)) {
47164             return;
47165         }
47166         var n = cn.split('-').pop();
47167         var ans = this.footerEls;
47168         var sel = ans[n];
47169         
47170          // pick
47171         var range = this.editorcore.createRange();
47172         
47173         range.selectNodeContents(sel);
47174         //range.selectNode(sel);
47175         
47176         
47177         var selection = this.editorcore.getSelection();
47178         selection.removeAllRanges();
47179         selection.addRange(range);
47180         
47181         
47182         
47183         this.updateToolbar(null, null, sel);
47184         
47185         
47186     }
47187     
47188     
47189     
47190     
47191     
47192 });
47193
47194
47195
47196
47197
47198 /*
47199  * Based on:
47200  * Ext JS Library 1.1.1
47201  * Copyright(c) 2006-2007, Ext JS, LLC.
47202  *
47203  * Originally Released Under LGPL - original licence link has changed is not relivant.
47204  *
47205  * Fork - LGPL
47206  * <script type="text/javascript">
47207  */
47208  
47209 /**
47210  * @class Roo.form.BasicForm
47211  * @extends Roo.util.Observable
47212  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47213  * @constructor
47214  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47215  * @param {Object} config Configuration options
47216  */
47217 Roo.form.BasicForm = function(el, config){
47218     this.allItems = [];
47219     this.childForms = [];
47220     Roo.apply(this, config);
47221     /*
47222      * The Roo.form.Field items in this form.
47223      * @type MixedCollection
47224      */
47225      
47226      
47227     this.items = new Roo.util.MixedCollection(false, function(o){
47228         return o.id || (o.id = Roo.id());
47229     });
47230     this.addEvents({
47231         /**
47232          * @event beforeaction
47233          * Fires before any action is performed. Return false to cancel the action.
47234          * @param {Form} this
47235          * @param {Action} action The action to be performed
47236          */
47237         beforeaction: true,
47238         /**
47239          * @event actionfailed
47240          * Fires when an action fails.
47241          * @param {Form} this
47242          * @param {Action} action The action that failed
47243          */
47244         actionfailed : true,
47245         /**
47246          * @event actioncomplete
47247          * Fires when an action is completed.
47248          * @param {Form} this
47249          * @param {Action} action The action that completed
47250          */
47251         actioncomplete : true
47252     });
47253     if(el){
47254         this.initEl(el);
47255     }
47256     Roo.form.BasicForm.superclass.constructor.call(this);
47257     
47258     Roo.form.BasicForm.popover.apply();
47259 };
47260
47261 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47262     /**
47263      * @cfg {String} method
47264      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47265      */
47266     /**
47267      * @cfg {DataReader} reader
47268      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47269      * This is optional as there is built-in support for processing JSON.
47270      */
47271     /**
47272      * @cfg {DataReader} errorReader
47273      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47274      * This is completely optional as there is built-in support for processing JSON.
47275      */
47276     /**
47277      * @cfg {String} url
47278      * The URL to use for form actions if one isn't supplied in the action options.
47279      */
47280     /**
47281      * @cfg {Boolean} fileUpload
47282      * Set to true if this form is a file upload.
47283      */
47284      
47285     /**
47286      * @cfg {Object} baseParams
47287      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47288      */
47289      /**
47290      
47291     /**
47292      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47293      */
47294     timeout: 30,
47295
47296     // private
47297     activeAction : null,
47298
47299     /**
47300      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47301      * or setValues() data instead of when the form was first created.
47302      */
47303     trackResetOnLoad : false,
47304     
47305     
47306     /**
47307      * childForms - used for multi-tab forms
47308      * @type {Array}
47309      */
47310     childForms : false,
47311     
47312     /**
47313      * allItems - full list of fields.
47314      * @type {Array}
47315      */
47316     allItems : false,
47317     
47318     /**
47319      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47320      * element by passing it or its id or mask the form itself by passing in true.
47321      * @type Mixed
47322      */
47323     waitMsgTarget : false,
47324     
47325     /**
47326      * @type Boolean
47327      */
47328     disableMask : false,
47329     
47330     /**
47331      * @cfg {Boolean} errorMask (true|false) default false
47332      */
47333     errorMask : false,
47334     
47335     /**
47336      * @cfg {Number} maskOffset Default 100
47337      */
47338     maskOffset : 100,
47339
47340     // private
47341     initEl : function(el){
47342         this.el = Roo.get(el);
47343         this.id = this.el.id || Roo.id();
47344         this.el.on('submit', this.onSubmit, this);
47345         this.el.addClass('x-form');
47346     },
47347
47348     // private
47349     onSubmit : function(e){
47350         e.stopEvent();
47351     },
47352
47353     /**
47354      * Returns true if client-side validation on the form is successful.
47355      * @return Boolean
47356      */
47357     isValid : function(){
47358         var valid = true;
47359         var target = false;
47360         this.items.each(function(f){
47361             if(f.validate()){
47362                 return;
47363             }
47364             
47365             valid = false;
47366                 
47367             if(!target && f.el.isVisible(true)){
47368                 target = f;
47369             }
47370         });
47371         
47372         if(this.errorMask && !valid){
47373             Roo.form.BasicForm.popover.mask(this, target);
47374         }
47375         
47376         return valid;
47377     },
47378
47379     /**
47380      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47381      * @return Boolean
47382      */
47383     isDirty : function(){
47384         var dirty = false;
47385         this.items.each(function(f){
47386            if(f.isDirty()){
47387                dirty = true;
47388                return false;
47389            }
47390         });
47391         return dirty;
47392     },
47393     
47394     /**
47395      * Returns true if any fields in this form have changed since their original load. (New version)
47396      * @return Boolean
47397      */
47398     
47399     hasChanged : function()
47400     {
47401         var dirty = false;
47402         this.items.each(function(f){
47403            if(f.hasChanged()){
47404                dirty = true;
47405                return false;
47406            }
47407         });
47408         return dirty;
47409         
47410     },
47411     /**
47412      * Resets all hasChanged to 'false' -
47413      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47414      * So hasChanged storage is only to be used for this purpose
47415      * @return Boolean
47416      */
47417     resetHasChanged : function()
47418     {
47419         this.items.each(function(f){
47420            f.resetHasChanged();
47421         });
47422         
47423     },
47424     
47425     
47426     /**
47427      * Performs a predefined action (submit or load) or custom actions you define on this form.
47428      * @param {String} actionName The name of the action type
47429      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47430      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47431      * accept other config options):
47432      * <pre>
47433 Property          Type             Description
47434 ----------------  ---------------  ----------------------------------------------------------------------------------
47435 url               String           The url for the action (defaults to the form's url)
47436 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47437 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47438 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47439                                    validate the form on the client (defaults to false)
47440      * </pre>
47441      * @return {BasicForm} this
47442      */
47443     doAction : function(action, options){
47444         if(typeof action == 'string'){
47445             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47446         }
47447         if(this.fireEvent('beforeaction', this, action) !== false){
47448             this.beforeAction(action);
47449             action.run.defer(100, action);
47450         }
47451         return this;
47452     },
47453
47454     /**
47455      * Shortcut to do a submit action.
47456      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47457      * @return {BasicForm} this
47458      */
47459     submit : function(options){
47460         this.doAction('submit', options);
47461         return this;
47462     },
47463
47464     /**
47465      * Shortcut to do a load action.
47466      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47467      * @return {BasicForm} this
47468      */
47469     load : function(options){
47470         this.doAction('load', options);
47471         return this;
47472     },
47473
47474     /**
47475      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47476      * @param {Record} record The record to edit
47477      * @return {BasicForm} this
47478      */
47479     updateRecord : function(record){
47480         record.beginEdit();
47481         var fs = record.fields;
47482         fs.each(function(f){
47483             var field = this.findField(f.name);
47484             if(field){
47485                 record.set(f.name, field.getValue());
47486             }
47487         }, this);
47488         record.endEdit();
47489         return this;
47490     },
47491
47492     /**
47493      * Loads an Roo.data.Record into this form.
47494      * @param {Record} record The record to load
47495      * @return {BasicForm} this
47496      */
47497     loadRecord : function(record){
47498         this.setValues(record.data);
47499         return this;
47500     },
47501
47502     // private
47503     beforeAction : function(action){
47504         var o = action.options;
47505         
47506         if(!this.disableMask) {
47507             if(this.waitMsgTarget === true){
47508                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47509             }else if(this.waitMsgTarget){
47510                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47511                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47512             }else {
47513                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47514             }
47515         }
47516         
47517          
47518     },
47519
47520     // private
47521     afterAction : function(action, success){
47522         this.activeAction = null;
47523         var o = action.options;
47524         
47525         if(!this.disableMask) {
47526             if(this.waitMsgTarget === true){
47527                 this.el.unmask();
47528             }else if(this.waitMsgTarget){
47529                 this.waitMsgTarget.unmask();
47530             }else{
47531                 Roo.MessageBox.updateProgress(1);
47532                 Roo.MessageBox.hide();
47533             }
47534         }
47535         
47536         if(success){
47537             if(o.reset){
47538                 this.reset();
47539             }
47540             Roo.callback(o.success, o.scope, [this, action]);
47541             this.fireEvent('actioncomplete', this, action);
47542             
47543         }else{
47544             
47545             // failure condition..
47546             // we have a scenario where updates need confirming.
47547             // eg. if a locking scenario exists..
47548             // we look for { errors : { needs_confirm : true }} in the response.
47549             if (
47550                 (typeof(action.result) != 'undefined')  &&
47551                 (typeof(action.result.errors) != 'undefined')  &&
47552                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47553            ){
47554                 var _t = this;
47555                 Roo.MessageBox.confirm(
47556                     "Change requires confirmation",
47557                     action.result.errorMsg,
47558                     function(r) {
47559                         if (r != 'yes') {
47560                             return;
47561                         }
47562                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47563                     }
47564                     
47565                 );
47566                 
47567                 
47568                 
47569                 return;
47570             }
47571             
47572             Roo.callback(o.failure, o.scope, [this, action]);
47573             // show an error message if no failed handler is set..
47574             if (!this.hasListener('actionfailed')) {
47575                 Roo.MessageBox.alert("Error",
47576                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47577                         action.result.errorMsg :
47578                         "Saving Failed, please check your entries or try again"
47579                 );
47580             }
47581             
47582             this.fireEvent('actionfailed', this, action);
47583         }
47584         
47585     },
47586
47587     /**
47588      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47589      * @param {String} id The value to search for
47590      * @return Field
47591      */
47592     findField : function(id){
47593         var field = this.items.get(id);
47594         if(!field){
47595             this.items.each(function(f){
47596                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47597                     field = f;
47598                     return false;
47599                 }
47600             });
47601         }
47602         return field || null;
47603     },
47604
47605     /**
47606      * Add a secondary form to this one, 
47607      * Used to provide tabbed forms. One form is primary, with hidden values 
47608      * which mirror the elements from the other forms.
47609      * 
47610      * @param {Roo.form.Form} form to add.
47611      * 
47612      */
47613     addForm : function(form)
47614     {
47615        
47616         if (this.childForms.indexOf(form) > -1) {
47617             // already added..
47618             return;
47619         }
47620         this.childForms.push(form);
47621         var n = '';
47622         Roo.each(form.allItems, function (fe) {
47623             
47624             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47625             if (this.findField(n)) { // already added..
47626                 return;
47627             }
47628             var add = new Roo.form.Hidden({
47629                 name : n
47630             });
47631             add.render(this.el);
47632             
47633             this.add( add );
47634         }, this);
47635         
47636     },
47637     /**
47638      * Mark fields in this form invalid in bulk.
47639      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47640      * @return {BasicForm} this
47641      */
47642     markInvalid : function(errors){
47643         if(errors instanceof Array){
47644             for(var i = 0, len = errors.length; i < len; i++){
47645                 var fieldError = errors[i];
47646                 var f = this.findField(fieldError.id);
47647                 if(f){
47648                     f.markInvalid(fieldError.msg);
47649                 }
47650             }
47651         }else{
47652             var field, id;
47653             for(id in errors){
47654                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47655                     field.markInvalid(errors[id]);
47656                 }
47657             }
47658         }
47659         Roo.each(this.childForms || [], function (f) {
47660             f.markInvalid(errors);
47661         });
47662         
47663         return this;
47664     },
47665
47666     /**
47667      * Set values for fields in this form in bulk.
47668      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
47669      * @return {BasicForm} this
47670      */
47671     setValues : function(values){
47672         if(values instanceof Array){ // array of objects
47673             for(var i = 0, len = values.length; i < len; i++){
47674                 var v = values[i];
47675                 var f = this.findField(v.id);
47676                 if(f){
47677                     f.setValue(v.value);
47678                     if(this.trackResetOnLoad){
47679                         f.originalValue = f.getValue();
47680                     }
47681                 }
47682             }
47683         }else{ // object hash
47684             var field, id;
47685             for(id in values){
47686                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47687                     
47688                     if (field.setFromData && 
47689                         field.valueField && 
47690                         field.displayField &&
47691                         // combos' with local stores can 
47692                         // be queried via setValue()
47693                         // to set their value..
47694                         (field.store && !field.store.isLocal)
47695                         ) {
47696                         // it's a combo
47697                         var sd = { };
47698                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47699                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47700                         field.setFromData(sd);
47701                         
47702                     } else {
47703                         field.setValue(values[id]);
47704                     }
47705                     
47706                     
47707                     if(this.trackResetOnLoad){
47708                         field.originalValue = field.getValue();
47709                     }
47710                 }
47711             }
47712         }
47713         this.resetHasChanged();
47714         
47715         
47716         Roo.each(this.childForms || [], function (f) {
47717             f.setValues(values);
47718             f.resetHasChanged();
47719         });
47720                 
47721         return this;
47722     },
47723  
47724     /**
47725      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47726      * they are returned as an array.
47727      * @param {Boolean} asString
47728      * @return {Object}
47729      */
47730     getValues : function(asString){
47731         if (this.childForms) {
47732             // copy values from the child forms
47733             Roo.each(this.childForms, function (f) {
47734                 this.setValues(f.getValues());
47735             }, this);
47736         }
47737         
47738         // use formdata
47739         if (typeof(FormData) != 'undefined' && asString !== true) {
47740             var fd = (new FormData(this.el.dom)).entries();
47741             var ret = {};
47742             var ent = fd.next();
47743             while (!ent.done) {
47744                 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
47745                 ent = fd.next();
47746             };
47747             return ret;
47748         }
47749         
47750         
47751         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47752         if(asString === true){
47753             return fs;
47754         }
47755         return Roo.urlDecode(fs);
47756     },
47757     
47758     /**
47759      * Returns the fields in this form as an object with key/value pairs. 
47760      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47761      * @return {Object}
47762      */
47763     getFieldValues : function(with_hidden)
47764     {
47765         if (this.childForms) {
47766             // copy values from the child forms
47767             // should this call getFieldValues - probably not as we do not currently copy
47768             // hidden fields when we generate..
47769             Roo.each(this.childForms, function (f) {
47770                 this.setValues(f.getValues());
47771             }, this);
47772         }
47773         
47774         var ret = {};
47775         this.items.each(function(f){
47776             if (!f.getName()) {
47777                 return;
47778             }
47779             var v = f.getValue();
47780             if (f.inputType =='radio') {
47781                 if (typeof(ret[f.getName()]) == 'undefined') {
47782                     ret[f.getName()] = ''; // empty..
47783                 }
47784                 
47785                 if (!f.el.dom.checked) {
47786                     return;
47787                     
47788                 }
47789                 v = f.el.dom.value;
47790                 
47791             }
47792             
47793             // not sure if this supported any more..
47794             if ((typeof(v) == 'object') && f.getRawValue) {
47795                 v = f.getRawValue() ; // dates..
47796             }
47797             // combo boxes where name != hiddenName...
47798             if (f.name != f.getName()) {
47799                 ret[f.name] = f.getRawValue();
47800             }
47801             ret[f.getName()] = v;
47802         });
47803         
47804         return ret;
47805     },
47806
47807     /**
47808      * Clears all invalid messages in this form.
47809      * @return {BasicForm} this
47810      */
47811     clearInvalid : function(){
47812         this.items.each(function(f){
47813            f.clearInvalid();
47814         });
47815         
47816         Roo.each(this.childForms || [], function (f) {
47817             f.clearInvalid();
47818         });
47819         
47820         
47821         return this;
47822     },
47823
47824     /**
47825      * Resets this form.
47826      * @return {BasicForm} this
47827      */
47828     reset : function(){
47829         this.items.each(function(f){
47830             f.reset();
47831         });
47832         
47833         Roo.each(this.childForms || [], function (f) {
47834             f.reset();
47835         });
47836         this.resetHasChanged();
47837         
47838         return this;
47839     },
47840
47841     /**
47842      * Add Roo.form components to this form.
47843      * @param {Field} field1
47844      * @param {Field} field2 (optional)
47845      * @param {Field} etc (optional)
47846      * @return {BasicForm} this
47847      */
47848     add : function(){
47849         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47850         return this;
47851     },
47852
47853
47854     /**
47855      * Removes a field from the items collection (does NOT remove its markup).
47856      * @param {Field} field
47857      * @return {BasicForm} this
47858      */
47859     remove : function(field){
47860         this.items.remove(field);
47861         return this;
47862     },
47863
47864     /**
47865      * Looks at the fields in this form, checks them for an id attribute,
47866      * and calls applyTo on the existing dom element with that id.
47867      * @return {BasicForm} this
47868      */
47869     render : function(){
47870         this.items.each(function(f){
47871             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47872                 f.applyTo(f.id);
47873             }
47874         });
47875         return this;
47876     },
47877
47878     /**
47879      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47880      * @param {Object} values
47881      * @return {BasicForm} this
47882      */
47883     applyToFields : function(o){
47884         this.items.each(function(f){
47885            Roo.apply(f, o);
47886         });
47887         return this;
47888     },
47889
47890     /**
47891      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47892      * @param {Object} values
47893      * @return {BasicForm} this
47894      */
47895     applyIfToFields : function(o){
47896         this.items.each(function(f){
47897            Roo.applyIf(f, o);
47898         });
47899         return this;
47900     }
47901 });
47902
47903 // back compat
47904 Roo.BasicForm = Roo.form.BasicForm;
47905
47906 Roo.apply(Roo.form.BasicForm, {
47907     
47908     popover : {
47909         
47910         padding : 5,
47911         
47912         isApplied : false,
47913         
47914         isMasked : false,
47915         
47916         form : false,
47917         
47918         target : false,
47919         
47920         intervalID : false,
47921         
47922         maskEl : false,
47923         
47924         apply : function()
47925         {
47926             if(this.isApplied){
47927                 return;
47928             }
47929             
47930             this.maskEl = {
47931                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
47932                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
47933                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
47934                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
47935             };
47936             
47937             this.maskEl.top.enableDisplayMode("block");
47938             this.maskEl.left.enableDisplayMode("block");
47939             this.maskEl.bottom.enableDisplayMode("block");
47940             this.maskEl.right.enableDisplayMode("block");
47941             
47942             Roo.get(document.body).on('click', function(){
47943                 this.unmask();
47944             }, this);
47945             
47946             Roo.get(document.body).on('touchstart', function(){
47947                 this.unmask();
47948             }, this);
47949             
47950             this.isApplied = true
47951         },
47952         
47953         mask : function(form, target)
47954         {
47955             this.form = form;
47956             
47957             this.target = target;
47958             
47959             if(!this.form.errorMask || !target.el){
47960                 return;
47961             }
47962             
47963             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
47964             
47965             var ot = this.target.el.calcOffsetsTo(scrollable);
47966             
47967             var scrollTo = ot[1] - this.form.maskOffset;
47968             
47969             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
47970             
47971             scrollable.scrollTo('top', scrollTo);
47972             
47973             var el = this.target.wrap || this.target.el;
47974             
47975             var box = el.getBox();
47976             
47977             this.maskEl.top.setStyle('position', 'absolute');
47978             this.maskEl.top.setStyle('z-index', 10000);
47979             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
47980             this.maskEl.top.setLeft(0);
47981             this.maskEl.top.setTop(0);
47982             this.maskEl.top.show();
47983             
47984             this.maskEl.left.setStyle('position', 'absolute');
47985             this.maskEl.left.setStyle('z-index', 10000);
47986             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
47987             this.maskEl.left.setLeft(0);
47988             this.maskEl.left.setTop(box.y - this.padding);
47989             this.maskEl.left.show();
47990
47991             this.maskEl.bottom.setStyle('position', 'absolute');
47992             this.maskEl.bottom.setStyle('z-index', 10000);
47993             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
47994             this.maskEl.bottom.setLeft(0);
47995             this.maskEl.bottom.setTop(box.bottom + this.padding);
47996             this.maskEl.bottom.show();
47997
47998             this.maskEl.right.setStyle('position', 'absolute');
47999             this.maskEl.right.setStyle('z-index', 10000);
48000             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48001             this.maskEl.right.setLeft(box.right + this.padding);
48002             this.maskEl.right.setTop(box.y - this.padding);
48003             this.maskEl.right.show();
48004
48005             this.intervalID = window.setInterval(function() {
48006                 Roo.form.BasicForm.popover.unmask();
48007             }, 10000);
48008
48009             window.onwheel = function(){ return false;};
48010             
48011             (function(){ this.isMasked = true; }).defer(500, this);
48012             
48013         },
48014         
48015         unmask : function()
48016         {
48017             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48018                 return;
48019             }
48020             
48021             this.maskEl.top.setStyle('position', 'absolute');
48022             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48023             this.maskEl.top.hide();
48024
48025             this.maskEl.left.setStyle('position', 'absolute');
48026             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48027             this.maskEl.left.hide();
48028
48029             this.maskEl.bottom.setStyle('position', 'absolute');
48030             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48031             this.maskEl.bottom.hide();
48032
48033             this.maskEl.right.setStyle('position', 'absolute');
48034             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48035             this.maskEl.right.hide();
48036             
48037             window.onwheel = function(){ return true;};
48038             
48039             if(this.intervalID){
48040                 window.clearInterval(this.intervalID);
48041                 this.intervalID = false;
48042             }
48043             
48044             this.isMasked = false;
48045             
48046         }
48047         
48048     }
48049     
48050 });/*
48051  * Based on:
48052  * Ext JS Library 1.1.1
48053  * Copyright(c) 2006-2007, Ext JS, LLC.
48054  *
48055  * Originally Released Under LGPL - original licence link has changed is not relivant.
48056  *
48057  * Fork - LGPL
48058  * <script type="text/javascript">
48059  */
48060
48061 /**
48062  * @class Roo.form.Form
48063  * @extends Roo.form.BasicForm
48064  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48065  * @constructor
48066  * @param {Object} config Configuration options
48067  */
48068 Roo.form.Form = function(config){
48069     var xitems =  [];
48070     if (config.items) {
48071         xitems = config.items;
48072         delete config.items;
48073     }
48074    
48075     
48076     Roo.form.Form.superclass.constructor.call(this, null, config);
48077     this.url = this.url || this.action;
48078     if(!this.root){
48079         this.root = new Roo.form.Layout(Roo.applyIf({
48080             id: Roo.id()
48081         }, config));
48082     }
48083     this.active = this.root;
48084     /**
48085      * Array of all the buttons that have been added to this form via {@link addButton}
48086      * @type Array
48087      */
48088     this.buttons = [];
48089     this.allItems = [];
48090     this.addEvents({
48091         /**
48092          * @event clientvalidation
48093          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48094          * @param {Form} this
48095          * @param {Boolean} valid true if the form has passed client-side validation
48096          */
48097         clientvalidation: true,
48098         /**
48099          * @event rendered
48100          * Fires when the form is rendered
48101          * @param {Roo.form.Form} form
48102          */
48103         rendered : true
48104     });
48105     
48106     if (this.progressUrl) {
48107             // push a hidden field onto the list of fields..
48108             this.addxtype( {
48109                     xns: Roo.form, 
48110                     xtype : 'Hidden', 
48111                     name : 'UPLOAD_IDENTIFIER' 
48112             });
48113         }
48114         
48115     
48116     Roo.each(xitems, this.addxtype, this);
48117     
48118 };
48119
48120 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48121     /**
48122      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48123      */
48124     /**
48125      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48126      */
48127     /**
48128      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48129      */
48130     buttonAlign:'center',
48131
48132     /**
48133      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48134      */
48135     minButtonWidth:75,
48136
48137     /**
48138      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48139      * This property cascades to child containers if not set.
48140      */
48141     labelAlign:'left',
48142
48143     /**
48144      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48145      * fires a looping event with that state. This is required to bind buttons to the valid
48146      * state using the config value formBind:true on the button.
48147      */
48148     monitorValid : false,
48149
48150     /**
48151      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48152      */
48153     monitorPoll : 200,
48154     
48155     /**
48156      * @cfg {String} progressUrl - Url to return progress data 
48157      */
48158     
48159     progressUrl : false,
48160     /**
48161      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48162      * sending a formdata with extra parameters - eg uploaded elements.
48163      */
48164     
48165     formData : false,
48166     
48167     /**
48168      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48169      * fields are added and the column is closed. If no fields are passed the column remains open
48170      * until end() is called.
48171      * @param {Object} config The config to pass to the column
48172      * @param {Field} field1 (optional)
48173      * @param {Field} field2 (optional)
48174      * @param {Field} etc (optional)
48175      * @return Column The column container object
48176      */
48177     column : function(c){
48178         var col = new Roo.form.Column(c);
48179         this.start(col);
48180         if(arguments.length > 1){ // duplicate code required because of Opera
48181             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48182             this.end();
48183         }
48184         return col;
48185     },
48186
48187     /**
48188      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48189      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48190      * until end() is called.
48191      * @param {Object} config The config to pass to the fieldset
48192      * @param {Field} field1 (optional)
48193      * @param {Field} field2 (optional)
48194      * @param {Field} etc (optional)
48195      * @return FieldSet The fieldset container object
48196      */
48197     fieldset : function(c){
48198         var fs = new Roo.form.FieldSet(c);
48199         this.start(fs);
48200         if(arguments.length > 1){ // duplicate code required because of Opera
48201             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48202             this.end();
48203         }
48204         return fs;
48205     },
48206
48207     /**
48208      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48209      * fields are added and the container is closed. If no fields are passed the container remains open
48210      * until end() is called.
48211      * @param {Object} config The config to pass to the Layout
48212      * @param {Field} field1 (optional)
48213      * @param {Field} field2 (optional)
48214      * @param {Field} etc (optional)
48215      * @return Layout The container object
48216      */
48217     container : function(c){
48218         var l = new Roo.form.Layout(c);
48219         this.start(l);
48220         if(arguments.length > 1){ // duplicate code required because of Opera
48221             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48222             this.end();
48223         }
48224         return l;
48225     },
48226
48227     /**
48228      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48229      * @param {Object} container A Roo.form.Layout or subclass of Layout
48230      * @return {Form} this
48231      */
48232     start : function(c){
48233         // cascade label info
48234         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48235         this.active.stack.push(c);
48236         c.ownerCt = this.active;
48237         this.active = c;
48238         return this;
48239     },
48240
48241     /**
48242      * Closes the current open container
48243      * @return {Form} this
48244      */
48245     end : function(){
48246         if(this.active == this.root){
48247             return this;
48248         }
48249         this.active = this.active.ownerCt;
48250         return this;
48251     },
48252
48253     /**
48254      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48255      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48256      * as the label of the field.
48257      * @param {Field} field1
48258      * @param {Field} field2 (optional)
48259      * @param {Field} etc. (optional)
48260      * @return {Form} this
48261      */
48262     add : function(){
48263         this.active.stack.push.apply(this.active.stack, arguments);
48264         this.allItems.push.apply(this.allItems,arguments);
48265         var r = [];
48266         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48267             if(a[i].isFormField){
48268                 r.push(a[i]);
48269             }
48270         }
48271         if(r.length > 0){
48272             Roo.form.Form.superclass.add.apply(this, r);
48273         }
48274         return this;
48275     },
48276     
48277
48278     
48279     
48280     
48281      /**
48282      * Find any element that has been added to a form, using it's ID or name
48283      * This can include framesets, columns etc. along with regular fields..
48284      * @param {String} id - id or name to find.
48285      
48286      * @return {Element} e - or false if nothing found.
48287      */
48288     findbyId : function(id)
48289     {
48290         var ret = false;
48291         if (!id) {
48292             return ret;
48293         }
48294         Roo.each(this.allItems, function(f){
48295             if (f.id == id || f.name == id ){
48296                 ret = f;
48297                 return false;
48298             }
48299         });
48300         return ret;
48301     },
48302
48303     
48304     
48305     /**
48306      * Render this form into the passed container. This should only be called once!
48307      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48308      * @return {Form} this
48309      */
48310     render : function(ct)
48311     {
48312         
48313         
48314         
48315         ct = Roo.get(ct);
48316         var o = this.autoCreate || {
48317             tag: 'form',
48318             method : this.method || 'POST',
48319             id : this.id || Roo.id()
48320         };
48321         this.initEl(ct.createChild(o));
48322
48323         this.root.render(this.el);
48324         
48325        
48326              
48327         this.items.each(function(f){
48328             f.render('x-form-el-'+f.id);
48329         });
48330
48331         if(this.buttons.length > 0){
48332             // tables are required to maintain order and for correct IE layout
48333             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48334                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48335                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48336             }}, null, true);
48337             var tr = tb.getElementsByTagName('tr')[0];
48338             for(var i = 0, len = this.buttons.length; i < len; i++) {
48339                 var b = this.buttons[i];
48340                 var td = document.createElement('td');
48341                 td.className = 'x-form-btn-td';
48342                 b.render(tr.appendChild(td));
48343             }
48344         }
48345         if(this.monitorValid){ // initialize after render
48346             this.startMonitoring();
48347         }
48348         this.fireEvent('rendered', this);
48349         return this;
48350     },
48351
48352     /**
48353      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48354      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48355      * object or a valid Roo.DomHelper element config
48356      * @param {Function} handler The function called when the button is clicked
48357      * @param {Object} scope (optional) The scope of the handler function
48358      * @return {Roo.Button}
48359      */
48360     addButton : function(config, handler, scope){
48361         var bc = {
48362             handler: handler,
48363             scope: scope,
48364             minWidth: this.minButtonWidth,
48365             hideParent:true
48366         };
48367         if(typeof config == "string"){
48368             bc.text = config;
48369         }else{
48370             Roo.apply(bc, config);
48371         }
48372         var btn = new Roo.Button(null, bc);
48373         this.buttons.push(btn);
48374         return btn;
48375     },
48376
48377      /**
48378      * Adds a series of form elements (using the xtype property as the factory method.
48379      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48380      * @param {Object} config 
48381      */
48382     
48383     addxtype : function()
48384     {
48385         var ar = Array.prototype.slice.call(arguments, 0);
48386         var ret = false;
48387         for(var i = 0; i < ar.length; i++) {
48388             if (!ar[i]) {
48389                 continue; // skip -- if this happends something invalid got sent, we 
48390                 // should ignore it, as basically that interface element will not show up
48391                 // and that should be pretty obvious!!
48392             }
48393             
48394             if (Roo.form[ar[i].xtype]) {
48395                 ar[i].form = this;
48396                 var fe = Roo.factory(ar[i], Roo.form);
48397                 if (!ret) {
48398                     ret = fe;
48399                 }
48400                 fe.form = this;
48401                 if (fe.store) {
48402                     fe.store.form = this;
48403                 }
48404                 if (fe.isLayout) {  
48405                          
48406                     this.start(fe);
48407                     this.allItems.push(fe);
48408                     if (fe.items && fe.addxtype) {
48409                         fe.addxtype.apply(fe, fe.items);
48410                         delete fe.items;
48411                     }
48412                      this.end();
48413                     continue;
48414                 }
48415                 
48416                 
48417                  
48418                 this.add(fe);
48419               //  console.log('adding ' + ar[i].xtype);
48420             }
48421             if (ar[i].xtype == 'Button') {  
48422                 //console.log('adding button');
48423                 //console.log(ar[i]);
48424                 this.addButton(ar[i]);
48425                 this.allItems.push(fe);
48426                 continue;
48427             }
48428             
48429             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48430                 alert('end is not supported on xtype any more, use items');
48431             //    this.end();
48432             //    //console.log('adding end');
48433             }
48434             
48435         }
48436         return ret;
48437     },
48438     
48439     /**
48440      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48441      * option "monitorValid"
48442      */
48443     startMonitoring : function(){
48444         if(!this.bound){
48445             this.bound = true;
48446             Roo.TaskMgr.start({
48447                 run : this.bindHandler,
48448                 interval : this.monitorPoll || 200,
48449                 scope: this
48450             });
48451         }
48452     },
48453
48454     /**
48455      * Stops monitoring of the valid state of this form
48456      */
48457     stopMonitoring : function(){
48458         this.bound = false;
48459     },
48460
48461     // private
48462     bindHandler : function(){
48463         if(!this.bound){
48464             return false; // stops binding
48465         }
48466         var valid = true;
48467         this.items.each(function(f){
48468             if(!f.isValid(true)){
48469                 valid = false;
48470                 return false;
48471             }
48472         });
48473         for(var i = 0, len = this.buttons.length; i < len; i++){
48474             var btn = this.buttons[i];
48475             if(btn.formBind === true && btn.disabled === valid){
48476                 btn.setDisabled(!valid);
48477             }
48478         }
48479         this.fireEvent('clientvalidation', this, valid);
48480     }
48481     
48482     
48483     
48484     
48485     
48486     
48487     
48488     
48489 });
48490
48491
48492 // back compat
48493 Roo.Form = Roo.form.Form;
48494 /*
48495  * Based on:
48496  * Ext JS Library 1.1.1
48497  * Copyright(c) 2006-2007, Ext JS, LLC.
48498  *
48499  * Originally Released Under LGPL - original licence link has changed is not relivant.
48500  *
48501  * Fork - LGPL
48502  * <script type="text/javascript">
48503  */
48504
48505 // as we use this in bootstrap.
48506 Roo.namespace('Roo.form');
48507  /**
48508  * @class Roo.form.Action
48509  * Internal Class used to handle form actions
48510  * @constructor
48511  * @param {Roo.form.BasicForm} el The form element or its id
48512  * @param {Object} config Configuration options
48513  */
48514
48515  
48516  
48517 // define the action interface
48518 Roo.form.Action = function(form, options){
48519     this.form = form;
48520     this.options = options || {};
48521 };
48522 /**
48523  * Client Validation Failed
48524  * @const 
48525  */
48526 Roo.form.Action.CLIENT_INVALID = 'client';
48527 /**
48528  * Server Validation Failed
48529  * @const 
48530  */
48531 Roo.form.Action.SERVER_INVALID = 'server';
48532  /**
48533  * Connect to Server Failed
48534  * @const 
48535  */
48536 Roo.form.Action.CONNECT_FAILURE = 'connect';
48537 /**
48538  * Reading Data from Server Failed
48539  * @const 
48540  */
48541 Roo.form.Action.LOAD_FAILURE = 'load';
48542
48543 Roo.form.Action.prototype = {
48544     type : 'default',
48545     failureType : undefined,
48546     response : undefined,
48547     result : undefined,
48548
48549     // interface method
48550     run : function(options){
48551
48552     },
48553
48554     // interface method
48555     success : function(response){
48556
48557     },
48558
48559     // interface method
48560     handleResponse : function(response){
48561
48562     },
48563
48564     // default connection failure
48565     failure : function(response){
48566         
48567         this.response = response;
48568         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48569         this.form.afterAction(this, false);
48570     },
48571
48572     processResponse : function(response){
48573         this.response = response;
48574         if(!response.responseText){
48575             return true;
48576         }
48577         this.result = this.handleResponse(response);
48578         return this.result;
48579     },
48580
48581     // utility functions used internally
48582     getUrl : function(appendParams){
48583         var url = this.options.url || this.form.url || this.form.el.dom.action;
48584         if(appendParams){
48585             var p = this.getParams();
48586             if(p){
48587                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48588             }
48589         }
48590         return url;
48591     },
48592
48593     getMethod : function(){
48594         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
48595     },
48596
48597     getParams : function(){
48598         var bp = this.form.baseParams;
48599         var p = this.options.params;
48600         if(p){
48601             if(typeof p == "object"){
48602                 p = Roo.urlEncode(Roo.applyIf(p, bp));
48603             }else if(typeof p == 'string' && bp){
48604                 p += '&' + Roo.urlEncode(bp);
48605             }
48606         }else if(bp){
48607             p = Roo.urlEncode(bp);
48608         }
48609         return p;
48610     },
48611
48612     createCallback : function(){
48613         return {
48614             success: this.success,
48615             failure: this.failure,
48616             scope: this,
48617             timeout: (this.form.timeout*1000),
48618             upload: this.form.fileUpload ? this.success : undefined
48619         };
48620     }
48621 };
48622
48623 Roo.form.Action.Submit = function(form, options){
48624     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
48625 };
48626
48627 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
48628     type : 'submit',
48629
48630     haveProgress : false,
48631     uploadComplete : false,
48632     
48633     // uploadProgress indicator.
48634     uploadProgress : function()
48635     {
48636         if (!this.form.progressUrl) {
48637             return;
48638         }
48639         
48640         if (!this.haveProgress) {
48641             Roo.MessageBox.progress("Uploading", "Uploading");
48642         }
48643         if (this.uploadComplete) {
48644            Roo.MessageBox.hide();
48645            return;
48646         }
48647         
48648         this.haveProgress = true;
48649    
48650         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
48651         
48652         var c = new Roo.data.Connection();
48653         c.request({
48654             url : this.form.progressUrl,
48655             params: {
48656                 id : uid
48657             },
48658             method: 'GET',
48659             success : function(req){
48660                //console.log(data);
48661                 var rdata = false;
48662                 var edata;
48663                 try  {
48664                    rdata = Roo.decode(req.responseText)
48665                 } catch (e) {
48666                     Roo.log("Invalid data from server..");
48667                     Roo.log(edata);
48668                     return;
48669                 }
48670                 if (!rdata || !rdata.success) {
48671                     Roo.log(rdata);
48672                     Roo.MessageBox.alert(Roo.encode(rdata));
48673                     return;
48674                 }
48675                 var data = rdata.data;
48676                 
48677                 if (this.uploadComplete) {
48678                    Roo.MessageBox.hide();
48679                    return;
48680                 }
48681                    
48682                 if (data){
48683                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
48684                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
48685                     );
48686                 }
48687                 this.uploadProgress.defer(2000,this);
48688             },
48689        
48690             failure: function(data) {
48691                 Roo.log('progress url failed ');
48692                 Roo.log(data);
48693             },
48694             scope : this
48695         });
48696            
48697     },
48698     
48699     
48700     run : function()
48701     {
48702         // run get Values on the form, so it syncs any secondary forms.
48703         this.form.getValues();
48704         
48705         var o = this.options;
48706         var method = this.getMethod();
48707         var isPost = method == 'POST';
48708         if(o.clientValidation === false || this.form.isValid()){
48709             
48710             if (this.form.progressUrl) {
48711                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
48712                     (new Date() * 1) + '' + Math.random());
48713                     
48714             } 
48715             
48716             
48717             Roo.Ajax.request(Roo.apply(this.createCallback(), {
48718                 form:this.form.el.dom,
48719                 url:this.getUrl(!isPost),
48720                 method: method,
48721                 params:isPost ? this.getParams() : null,
48722                 isUpload: this.form.fileUpload,
48723                 formData : this.form.formData
48724             }));
48725             
48726             this.uploadProgress();
48727
48728         }else if (o.clientValidation !== false){ // client validation failed
48729             this.failureType = Roo.form.Action.CLIENT_INVALID;
48730             this.form.afterAction(this, false);
48731         }
48732     },
48733
48734     success : function(response)
48735     {
48736         this.uploadComplete= true;
48737         if (this.haveProgress) {
48738             Roo.MessageBox.hide();
48739         }
48740         
48741         
48742         var result = this.processResponse(response);
48743         if(result === true || result.success){
48744             this.form.afterAction(this, true);
48745             return;
48746         }
48747         if(result.errors){
48748             this.form.markInvalid(result.errors);
48749             this.failureType = Roo.form.Action.SERVER_INVALID;
48750         }
48751         this.form.afterAction(this, false);
48752     },
48753     failure : function(response)
48754     {
48755         this.uploadComplete= true;
48756         if (this.haveProgress) {
48757             Roo.MessageBox.hide();
48758         }
48759         
48760         this.response = response;
48761         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48762         this.form.afterAction(this, false);
48763     },
48764     
48765     handleResponse : function(response){
48766         if(this.form.errorReader){
48767             var rs = this.form.errorReader.read(response);
48768             var errors = [];
48769             if(rs.records){
48770                 for(var i = 0, len = rs.records.length; i < len; i++) {
48771                     var r = rs.records[i];
48772                     errors[i] = r.data;
48773                 }
48774             }
48775             if(errors.length < 1){
48776                 errors = null;
48777             }
48778             return {
48779                 success : rs.success,
48780                 errors : errors
48781             };
48782         }
48783         var ret = false;
48784         try {
48785             ret = Roo.decode(response.responseText);
48786         } catch (e) {
48787             ret = {
48788                 success: false,
48789                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
48790                 errors : []
48791             };
48792         }
48793         return ret;
48794         
48795     }
48796 });
48797
48798
48799 Roo.form.Action.Load = function(form, options){
48800     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
48801     this.reader = this.form.reader;
48802 };
48803
48804 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
48805     type : 'load',
48806
48807     run : function(){
48808         
48809         Roo.Ajax.request(Roo.apply(
48810                 this.createCallback(), {
48811                     method:this.getMethod(),
48812                     url:this.getUrl(false),
48813                     params:this.getParams()
48814         }));
48815     },
48816
48817     success : function(response){
48818         
48819         var result = this.processResponse(response);
48820         if(result === true || !result.success || !result.data){
48821             this.failureType = Roo.form.Action.LOAD_FAILURE;
48822             this.form.afterAction(this, false);
48823             return;
48824         }
48825         this.form.clearInvalid();
48826         this.form.setValues(result.data);
48827         this.form.afterAction(this, true);
48828     },
48829
48830     handleResponse : function(response){
48831         if(this.form.reader){
48832             var rs = this.form.reader.read(response);
48833             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
48834             return {
48835                 success : rs.success,
48836                 data : data
48837             };
48838         }
48839         return Roo.decode(response.responseText);
48840     }
48841 });
48842
48843 Roo.form.Action.ACTION_TYPES = {
48844     'load' : Roo.form.Action.Load,
48845     'submit' : Roo.form.Action.Submit
48846 };/*
48847  * Based on:
48848  * Ext JS Library 1.1.1
48849  * Copyright(c) 2006-2007, Ext JS, LLC.
48850  *
48851  * Originally Released Under LGPL - original licence link has changed is not relivant.
48852  *
48853  * Fork - LGPL
48854  * <script type="text/javascript">
48855  */
48856  
48857 /**
48858  * @class Roo.form.Layout
48859  * @extends Roo.Component
48860  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
48861  * @constructor
48862  * @param {Object} config Configuration options
48863  */
48864 Roo.form.Layout = function(config){
48865     var xitems = [];
48866     if (config.items) {
48867         xitems = config.items;
48868         delete config.items;
48869     }
48870     Roo.form.Layout.superclass.constructor.call(this, config);
48871     this.stack = [];
48872     Roo.each(xitems, this.addxtype, this);
48873      
48874 };
48875
48876 Roo.extend(Roo.form.Layout, Roo.Component, {
48877     /**
48878      * @cfg {String/Object} autoCreate
48879      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48880      */
48881     /**
48882      * @cfg {String/Object/Function} style
48883      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48884      * a function which returns such a specification.
48885      */
48886     /**
48887      * @cfg {String} labelAlign
48888      * Valid values are "left," "top" and "right" (defaults to "left")
48889      */
48890     /**
48891      * @cfg {Number} labelWidth
48892      * Fixed width in pixels of all field labels (defaults to undefined)
48893      */
48894     /**
48895      * @cfg {Boolean} clear
48896      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48897      */
48898     clear : true,
48899     /**
48900      * @cfg {String} labelSeparator
48901      * The separator to use after field labels (defaults to ':')
48902      */
48903     labelSeparator : ':',
48904     /**
48905      * @cfg {Boolean} hideLabels
48906      * True to suppress the display of field labels in this layout (defaults to false)
48907      */
48908     hideLabels : false,
48909
48910     // private
48911     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48912     
48913     isLayout : true,
48914     
48915     // private
48916     onRender : function(ct, position){
48917         if(this.el){ // from markup
48918             this.el = Roo.get(this.el);
48919         }else {  // generate
48920             var cfg = this.getAutoCreate();
48921             this.el = ct.createChild(cfg, position);
48922         }
48923         if(this.style){
48924             this.el.applyStyles(this.style);
48925         }
48926         if(this.labelAlign){
48927             this.el.addClass('x-form-label-'+this.labelAlign);
48928         }
48929         if(this.hideLabels){
48930             this.labelStyle = "display:none";
48931             this.elementStyle = "padding-left:0;";
48932         }else{
48933             if(typeof this.labelWidth == 'number'){
48934                 this.labelStyle = "width:"+this.labelWidth+"px;";
48935                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48936             }
48937             if(this.labelAlign == 'top'){
48938                 this.labelStyle = "width:auto;";
48939                 this.elementStyle = "padding-left:0;";
48940             }
48941         }
48942         var stack = this.stack;
48943         var slen = stack.length;
48944         if(slen > 0){
48945             if(!this.fieldTpl){
48946                 var t = new Roo.Template(
48947                     '<div class="x-form-item {5}">',
48948                         '<label for="{0}" style="{2}">{1}{4}</label>',
48949                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48950                         '</div>',
48951                     '</div><div class="x-form-clear-left"></div>'
48952                 );
48953                 t.disableFormats = true;
48954                 t.compile();
48955                 Roo.form.Layout.prototype.fieldTpl = t;
48956             }
48957             for(var i = 0; i < slen; i++) {
48958                 if(stack[i].isFormField){
48959                     this.renderField(stack[i]);
48960                 }else{
48961                     this.renderComponent(stack[i]);
48962                 }
48963             }
48964         }
48965         if(this.clear){
48966             this.el.createChild({cls:'x-form-clear'});
48967         }
48968     },
48969
48970     // private
48971     renderField : function(f){
48972         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48973                f.id, //0
48974                f.fieldLabel, //1
48975                f.labelStyle||this.labelStyle||'', //2
48976                this.elementStyle||'', //3
48977                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48978                f.itemCls||this.itemCls||''  //5
48979        ], true).getPrevSibling());
48980     },
48981
48982     // private
48983     renderComponent : function(c){
48984         c.render(c.isLayout ? this.el : this.el.createChild());    
48985     },
48986     /**
48987      * Adds a object form elements (using the xtype property as the factory method.)
48988      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48989      * @param {Object} config 
48990      */
48991     addxtype : function(o)
48992     {
48993         // create the lement.
48994         o.form = this.form;
48995         var fe = Roo.factory(o, Roo.form);
48996         this.form.allItems.push(fe);
48997         this.stack.push(fe);
48998         
48999         if (fe.isFormField) {
49000             this.form.items.add(fe);
49001         }
49002          
49003         return fe;
49004     }
49005 });
49006
49007 /**
49008  * @class Roo.form.Column
49009  * @extends Roo.form.Layout
49010  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49011  * @constructor
49012  * @param {Object} config Configuration options
49013  */
49014 Roo.form.Column = function(config){
49015     Roo.form.Column.superclass.constructor.call(this, config);
49016 };
49017
49018 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49019     /**
49020      * @cfg {Number/String} width
49021      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49022      */
49023     /**
49024      * @cfg {String/Object} autoCreate
49025      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49026      */
49027
49028     // private
49029     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49030
49031     // private
49032     onRender : function(ct, position){
49033         Roo.form.Column.superclass.onRender.call(this, ct, position);
49034         if(this.width){
49035             this.el.setWidth(this.width);
49036         }
49037     }
49038 });
49039
49040
49041 /**
49042  * @class Roo.form.Row
49043  * @extends Roo.form.Layout
49044  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49045  * @constructor
49046  * @param {Object} config Configuration options
49047  */
49048
49049  
49050 Roo.form.Row = function(config){
49051     Roo.form.Row.superclass.constructor.call(this, config);
49052 };
49053  
49054 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49055       /**
49056      * @cfg {Number/String} width
49057      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49058      */
49059     /**
49060      * @cfg {Number/String} height
49061      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49062      */
49063     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49064     
49065     padWidth : 20,
49066     // private
49067     onRender : function(ct, position){
49068         //console.log('row render');
49069         if(!this.rowTpl){
49070             var t = new Roo.Template(
49071                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49072                     '<label for="{0}" style="{2}">{1}{4}</label>',
49073                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49074                     '</div>',
49075                 '</div>'
49076             );
49077             t.disableFormats = true;
49078             t.compile();
49079             Roo.form.Layout.prototype.rowTpl = t;
49080         }
49081         this.fieldTpl = this.rowTpl;
49082         
49083         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49084         var labelWidth = 100;
49085         
49086         if ((this.labelAlign != 'top')) {
49087             if (typeof this.labelWidth == 'number') {
49088                 labelWidth = this.labelWidth
49089             }
49090             this.padWidth =  20 + labelWidth;
49091             
49092         }
49093         
49094         Roo.form.Column.superclass.onRender.call(this, ct, position);
49095         if(this.width){
49096             this.el.setWidth(this.width);
49097         }
49098         if(this.height){
49099             this.el.setHeight(this.height);
49100         }
49101     },
49102     
49103     // private
49104     renderField : function(f){
49105         f.fieldEl = this.fieldTpl.append(this.el, [
49106                f.id, f.fieldLabel,
49107                f.labelStyle||this.labelStyle||'',
49108                this.elementStyle||'',
49109                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49110                f.itemCls||this.itemCls||'',
49111                f.width ? f.width + this.padWidth : 160 + this.padWidth
49112        ],true);
49113     }
49114 });
49115  
49116
49117 /**
49118  * @class Roo.form.FieldSet
49119  * @extends Roo.form.Layout
49120  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49121  * @constructor
49122  * @param {Object} config Configuration options
49123  */
49124 Roo.form.FieldSet = function(config){
49125     Roo.form.FieldSet.superclass.constructor.call(this, config);
49126 };
49127
49128 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49129     /**
49130      * @cfg {String} legend
49131      * The text to display as the legend for the FieldSet (defaults to '')
49132      */
49133     /**
49134      * @cfg {String/Object} autoCreate
49135      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49136      */
49137
49138     // private
49139     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49140
49141     // private
49142     onRender : function(ct, position){
49143         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49144         if(this.legend){
49145             this.setLegend(this.legend);
49146         }
49147     },
49148
49149     // private
49150     setLegend : function(text){
49151         if(this.rendered){
49152             this.el.child('legend').update(text);
49153         }
49154     }
49155 });/*
49156  * Based on:
49157  * Ext JS Library 1.1.1
49158  * Copyright(c) 2006-2007, Ext JS, LLC.
49159  *
49160  * Originally Released Under LGPL - original licence link has changed is not relivant.
49161  *
49162  * Fork - LGPL
49163  * <script type="text/javascript">
49164  */
49165 /**
49166  * @class Roo.form.VTypes
49167  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49168  * @singleton
49169  */
49170 Roo.form.VTypes = function(){
49171     // closure these in so they are only created once.
49172     var alpha = /^[a-zA-Z_]+$/;
49173     var alphanum = /^[a-zA-Z0-9_]+$/;
49174     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49175     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49176
49177     // All these messages and functions are configurable
49178     return {
49179         /**
49180          * The function used to validate email addresses
49181          * @param {String} value The email address
49182          */
49183         'email' : function(v){
49184             return email.test(v);
49185         },
49186         /**
49187          * The error text to display when the email validation function returns false
49188          * @type String
49189          */
49190         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49191         /**
49192          * The keystroke filter mask to be applied on email input
49193          * @type RegExp
49194          */
49195         'emailMask' : /[a-z0-9_\.\-@]/i,
49196
49197         /**
49198          * The function used to validate URLs
49199          * @param {String} value The URL
49200          */
49201         'url' : function(v){
49202             return url.test(v);
49203         },
49204         /**
49205          * The error text to display when the url validation function returns false
49206          * @type String
49207          */
49208         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49209         
49210         /**
49211          * The function used to validate alpha values
49212          * @param {String} value The value
49213          */
49214         'alpha' : function(v){
49215             return alpha.test(v);
49216         },
49217         /**
49218          * The error text to display when the alpha validation function returns false
49219          * @type String
49220          */
49221         'alphaText' : 'This field should only contain letters and _',
49222         /**
49223          * The keystroke filter mask to be applied on alpha input
49224          * @type RegExp
49225          */
49226         'alphaMask' : /[a-z_]/i,
49227
49228         /**
49229          * The function used to validate alphanumeric values
49230          * @param {String} value The value
49231          */
49232         'alphanum' : function(v){
49233             return alphanum.test(v);
49234         },
49235         /**
49236          * The error text to display when the alphanumeric validation function returns false
49237          * @type String
49238          */
49239         'alphanumText' : 'This field should only contain letters, numbers and _',
49240         /**
49241          * The keystroke filter mask to be applied on alphanumeric input
49242          * @type RegExp
49243          */
49244         'alphanumMask' : /[a-z0-9_]/i
49245     };
49246 }();//<script type="text/javascript">
49247
49248 /**
49249  * @class Roo.form.FCKeditor
49250  * @extends Roo.form.TextArea
49251  * Wrapper around the FCKEditor http://www.fckeditor.net
49252  * @constructor
49253  * Creates a new FCKeditor
49254  * @param {Object} config Configuration options
49255  */
49256 Roo.form.FCKeditor = function(config){
49257     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49258     this.addEvents({
49259          /**
49260          * @event editorinit
49261          * Fired when the editor is initialized - you can add extra handlers here..
49262          * @param {FCKeditor} this
49263          * @param {Object} the FCK object.
49264          */
49265         editorinit : true
49266     });
49267     
49268     
49269 };
49270 Roo.form.FCKeditor.editors = { };
49271 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49272 {
49273     //defaultAutoCreate : {
49274     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49275     //},
49276     // private
49277     /**
49278      * @cfg {Object} fck options - see fck manual for details.
49279      */
49280     fckconfig : false,
49281     
49282     /**
49283      * @cfg {Object} fck toolbar set (Basic or Default)
49284      */
49285     toolbarSet : 'Basic',
49286     /**
49287      * @cfg {Object} fck BasePath
49288      */ 
49289     basePath : '/fckeditor/',
49290     
49291     
49292     frame : false,
49293     
49294     value : '',
49295     
49296    
49297     onRender : function(ct, position)
49298     {
49299         if(!this.el){
49300             this.defaultAutoCreate = {
49301                 tag: "textarea",
49302                 style:"width:300px;height:60px;",
49303                 autocomplete: "new-password"
49304             };
49305         }
49306         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49307         /*
49308         if(this.grow){
49309             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49310             if(this.preventScrollbars){
49311                 this.el.setStyle("overflow", "hidden");
49312             }
49313             this.el.setHeight(this.growMin);
49314         }
49315         */
49316         //console.log('onrender' + this.getId() );
49317         Roo.form.FCKeditor.editors[this.getId()] = this;
49318          
49319
49320         this.replaceTextarea() ;
49321         
49322     },
49323     
49324     getEditor : function() {
49325         return this.fckEditor;
49326     },
49327     /**
49328      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49329      * @param {Mixed} value The value to set
49330      */
49331     
49332     
49333     setValue : function(value)
49334     {
49335         //console.log('setValue: ' + value);
49336         
49337         if(typeof(value) == 'undefined') { // not sure why this is happending...
49338             return;
49339         }
49340         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49341         
49342         //if(!this.el || !this.getEditor()) {
49343         //    this.value = value;
49344             //this.setValue.defer(100,this,[value]);    
49345         //    return;
49346         //} 
49347         
49348         if(!this.getEditor()) {
49349             return;
49350         }
49351         
49352         this.getEditor().SetData(value);
49353         
49354         //
49355
49356     },
49357
49358     /**
49359      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49360      * @return {Mixed} value The field value
49361      */
49362     getValue : function()
49363     {
49364         
49365         if (this.frame && this.frame.dom.style.display == 'none') {
49366             return Roo.form.FCKeditor.superclass.getValue.call(this);
49367         }
49368         
49369         if(!this.el || !this.getEditor()) {
49370            
49371            // this.getValue.defer(100,this); 
49372             return this.value;
49373         }
49374        
49375         
49376         var value=this.getEditor().GetData();
49377         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49378         return Roo.form.FCKeditor.superclass.getValue.call(this);
49379         
49380
49381     },
49382
49383     /**
49384      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49385      * @return {Mixed} value The field value
49386      */
49387     getRawValue : function()
49388     {
49389         if (this.frame && this.frame.dom.style.display == 'none') {
49390             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49391         }
49392         
49393         if(!this.el || !this.getEditor()) {
49394             //this.getRawValue.defer(100,this); 
49395             return this.value;
49396             return;
49397         }
49398         
49399         
49400         
49401         var value=this.getEditor().GetData();
49402         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49403         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49404          
49405     },
49406     
49407     setSize : function(w,h) {
49408         
49409         
49410         
49411         //if (this.frame && this.frame.dom.style.display == 'none') {
49412         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49413         //    return;
49414         //}
49415         //if(!this.el || !this.getEditor()) {
49416         //    this.setSize.defer(100,this, [w,h]); 
49417         //    return;
49418         //}
49419         
49420         
49421         
49422         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49423         
49424         this.frame.dom.setAttribute('width', w);
49425         this.frame.dom.setAttribute('height', h);
49426         this.frame.setSize(w,h);
49427         
49428     },
49429     
49430     toggleSourceEdit : function(value) {
49431         
49432       
49433          
49434         this.el.dom.style.display = value ? '' : 'none';
49435         this.frame.dom.style.display = value ?  'none' : '';
49436         
49437     },
49438     
49439     
49440     focus: function(tag)
49441     {
49442         if (this.frame.dom.style.display == 'none') {
49443             return Roo.form.FCKeditor.superclass.focus.call(this);
49444         }
49445         if(!this.el || !this.getEditor()) {
49446             this.focus.defer(100,this, [tag]); 
49447             return;
49448         }
49449         
49450         
49451         
49452         
49453         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49454         this.getEditor().Focus();
49455         if (tgs.length) {
49456             if (!this.getEditor().Selection.GetSelection()) {
49457                 this.focus.defer(100,this, [tag]); 
49458                 return;
49459             }
49460             
49461             
49462             var r = this.getEditor().EditorDocument.createRange();
49463             r.setStart(tgs[0],0);
49464             r.setEnd(tgs[0],0);
49465             this.getEditor().Selection.GetSelection().removeAllRanges();
49466             this.getEditor().Selection.GetSelection().addRange(r);
49467             this.getEditor().Focus();
49468         }
49469         
49470     },
49471     
49472     
49473     
49474     replaceTextarea : function()
49475     {
49476         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49477             return ;
49478         }
49479         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49480         //{
49481             // We must check the elements firstly using the Id and then the name.
49482         var oTextarea = document.getElementById( this.getId() );
49483         
49484         var colElementsByName = document.getElementsByName( this.getId() ) ;
49485          
49486         oTextarea.style.display = 'none' ;
49487
49488         if ( oTextarea.tabIndex ) {            
49489             this.TabIndex = oTextarea.tabIndex ;
49490         }
49491         
49492         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49493         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49494         this.frame = Roo.get(this.getId() + '___Frame')
49495     },
49496     
49497     _getConfigHtml : function()
49498     {
49499         var sConfig = '' ;
49500
49501         for ( var o in this.fckconfig ) {
49502             sConfig += sConfig.length > 0  ? '&amp;' : '';
49503             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49504         }
49505
49506         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49507     },
49508     
49509     
49510     _getIFrameHtml : function()
49511     {
49512         var sFile = 'fckeditor.html' ;
49513         /* no idea what this is about..
49514         try
49515         {
49516             if ( (/fcksource=true/i).test( window.top.location.search ) )
49517                 sFile = 'fckeditor.original.html' ;
49518         }
49519         catch (e) { 
49520         */
49521
49522         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49523         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49524         
49525         
49526         var html = '<iframe id="' + this.getId() +
49527             '___Frame" src="' + sLink +
49528             '" width="' + this.width +
49529             '" height="' + this.height + '"' +
49530             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49531             ' frameborder="0" scrolling="no"></iframe>' ;
49532
49533         return html ;
49534     },
49535     
49536     _insertHtmlBefore : function( html, element )
49537     {
49538         if ( element.insertAdjacentHTML )       {
49539             // IE
49540             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49541         } else { // Gecko
49542             var oRange = document.createRange() ;
49543             oRange.setStartBefore( element ) ;
49544             var oFragment = oRange.createContextualFragment( html );
49545             element.parentNode.insertBefore( oFragment, element ) ;
49546         }
49547     }
49548     
49549     
49550   
49551     
49552     
49553     
49554     
49555
49556 });
49557
49558 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49559
49560 function FCKeditor_OnComplete(editorInstance){
49561     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49562     f.fckEditor = editorInstance;
49563     //console.log("loaded");
49564     f.fireEvent('editorinit', f, editorInstance);
49565
49566   
49567
49568  
49569
49570
49571
49572
49573
49574
49575
49576
49577
49578
49579
49580
49581
49582
49583
49584 //<script type="text/javascript">
49585 /**
49586  * @class Roo.form.GridField
49587  * @extends Roo.form.Field
49588  * Embed a grid (or editable grid into a form)
49589  * STATUS ALPHA
49590  * 
49591  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
49592  * it needs 
49593  * xgrid.store = Roo.data.Store
49594  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
49595  * xgrid.store.reader = Roo.data.JsonReader 
49596  * 
49597  * 
49598  * @constructor
49599  * Creates a new GridField
49600  * @param {Object} config Configuration options
49601  */
49602 Roo.form.GridField = function(config){
49603     Roo.form.GridField.superclass.constructor.call(this, config);
49604      
49605 };
49606
49607 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
49608     /**
49609      * @cfg {Number} width  - used to restrict width of grid..
49610      */
49611     width : 100,
49612     /**
49613      * @cfg {Number} height - used to restrict height of grid..
49614      */
49615     height : 50,
49616      /**
49617      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
49618          * 
49619          *}
49620      */
49621     xgrid : false, 
49622     /**
49623      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49624      * {tag: "input", type: "checkbox", autocomplete: "off"})
49625      */
49626    // defaultAutoCreate : { tag: 'div' },
49627     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
49628     /**
49629      * @cfg {String} addTitle Text to include for adding a title.
49630      */
49631     addTitle : false,
49632     //
49633     onResize : function(){
49634         Roo.form.Field.superclass.onResize.apply(this, arguments);
49635     },
49636
49637     initEvents : function(){
49638         // Roo.form.Checkbox.superclass.initEvents.call(this);
49639         // has no events...
49640        
49641     },
49642
49643
49644     getResizeEl : function(){
49645         return this.wrap;
49646     },
49647
49648     getPositionEl : function(){
49649         return this.wrap;
49650     },
49651
49652     // private
49653     onRender : function(ct, position){
49654         
49655         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
49656         var style = this.style;
49657         delete this.style;
49658         
49659         Roo.form.GridField.superclass.onRender.call(this, ct, position);
49660         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
49661         this.viewEl = this.wrap.createChild({ tag: 'div' });
49662         if (style) {
49663             this.viewEl.applyStyles(style);
49664         }
49665         if (this.width) {
49666             this.viewEl.setWidth(this.width);
49667         }
49668         if (this.height) {
49669             this.viewEl.setHeight(this.height);
49670         }
49671         //if(this.inputValue !== undefined){
49672         //this.setValue(this.value);
49673         
49674         
49675         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
49676         
49677         
49678         this.grid.render();
49679         this.grid.getDataSource().on('remove', this.refreshValue, this);
49680         this.grid.getDataSource().on('update', this.refreshValue, this);
49681         this.grid.on('afteredit', this.refreshValue, this);
49682  
49683     },
49684      
49685     
49686     /**
49687      * Sets the value of the item. 
49688      * @param {String} either an object  or a string..
49689      */
49690     setValue : function(v){
49691         //this.value = v;
49692         v = v || []; // empty set..
49693         // this does not seem smart - it really only affects memoryproxy grids..
49694         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
49695             var ds = this.grid.getDataSource();
49696             // assumes a json reader..
49697             var data = {}
49698             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
49699             ds.loadData( data);
49700         }
49701         // clear selection so it does not get stale.
49702         if (this.grid.sm) { 
49703             this.grid.sm.clearSelections();
49704         }
49705         
49706         Roo.form.GridField.superclass.setValue.call(this, v);
49707         this.refreshValue();
49708         // should load data in the grid really....
49709     },
49710     
49711     // private
49712     refreshValue: function() {
49713          var val = [];
49714         this.grid.getDataSource().each(function(r) {
49715             val.push(r.data);
49716         });
49717         this.el.dom.value = Roo.encode(val);
49718     }
49719     
49720      
49721     
49722     
49723 });/*
49724  * Based on:
49725  * Ext JS Library 1.1.1
49726  * Copyright(c) 2006-2007, Ext JS, LLC.
49727  *
49728  * Originally Released Under LGPL - original licence link has changed is not relivant.
49729  *
49730  * Fork - LGPL
49731  * <script type="text/javascript">
49732  */
49733 /**
49734  * @class Roo.form.DisplayField
49735  * @extends Roo.form.Field
49736  * A generic Field to display non-editable data.
49737  * @cfg {Boolean} closable (true|false) default false
49738  * @constructor
49739  * Creates a new Display Field item.
49740  * @param {Object} config Configuration options
49741  */
49742 Roo.form.DisplayField = function(config){
49743     Roo.form.DisplayField.superclass.constructor.call(this, config);
49744     
49745     this.addEvents({
49746         /**
49747          * @event close
49748          * Fires after the click the close btn
49749              * @param {Roo.form.DisplayField} this
49750              */
49751         close : true
49752     });
49753 };
49754
49755 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
49756     inputType:      'hidden',
49757     allowBlank:     true,
49758     readOnly:         true,
49759     
49760  
49761     /**
49762      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49763      */
49764     focusClass : undefined,
49765     /**
49766      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49767      */
49768     fieldClass: 'x-form-field',
49769     
49770      /**
49771      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
49772      */
49773     valueRenderer: undefined,
49774     
49775     width: 100,
49776     /**
49777      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49778      * {tag: "input", type: "checkbox", autocomplete: "off"})
49779      */
49780      
49781  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
49782  
49783     closable : false,
49784     
49785     onResize : function(){
49786         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
49787         
49788     },
49789
49790     initEvents : function(){
49791         // Roo.form.Checkbox.superclass.initEvents.call(this);
49792         // has no events...
49793         
49794         if(this.closable){
49795             this.closeEl.on('click', this.onClose, this);
49796         }
49797        
49798     },
49799
49800
49801     getResizeEl : function(){
49802         return this.wrap;
49803     },
49804
49805     getPositionEl : function(){
49806         return this.wrap;
49807     },
49808
49809     // private
49810     onRender : function(ct, position){
49811         
49812         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
49813         //if(this.inputValue !== undefined){
49814         this.wrap = this.el.wrap();
49815         
49816         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
49817         
49818         if(this.closable){
49819             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
49820         }
49821         
49822         if (this.bodyStyle) {
49823             this.viewEl.applyStyles(this.bodyStyle);
49824         }
49825         //this.viewEl.setStyle('padding', '2px');
49826         
49827         this.setValue(this.value);
49828         
49829     },
49830 /*
49831     // private
49832     initValue : Roo.emptyFn,
49833
49834   */
49835
49836         // private
49837     onClick : function(){
49838         
49839     },
49840
49841     /**
49842      * Sets the checked state of the checkbox.
49843      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
49844      */
49845     setValue : function(v){
49846         this.value = v;
49847         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
49848         // this might be called before we have a dom element..
49849         if (!this.viewEl) {
49850             return;
49851         }
49852         this.viewEl.dom.innerHTML = html;
49853         Roo.form.DisplayField.superclass.setValue.call(this, v);
49854
49855     },
49856     
49857     onClose : function(e)
49858     {
49859         e.preventDefault();
49860         
49861         this.fireEvent('close', this);
49862     }
49863 });/*
49864  * 
49865  * Licence- LGPL
49866  * 
49867  */
49868
49869 /**
49870  * @class Roo.form.DayPicker
49871  * @extends Roo.form.Field
49872  * A Day picker show [M] [T] [W] ....
49873  * @constructor
49874  * Creates a new Day Picker
49875  * @param {Object} config Configuration options
49876  */
49877 Roo.form.DayPicker= function(config){
49878     Roo.form.DayPicker.superclass.constructor.call(this, config);
49879      
49880 };
49881
49882 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49883     /**
49884      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49885      */
49886     focusClass : undefined,
49887     /**
49888      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49889      */
49890     fieldClass: "x-form-field",
49891    
49892     /**
49893      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49894      * {tag: "input", type: "checkbox", autocomplete: "off"})
49895      */
49896     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49897     
49898    
49899     actionMode : 'viewEl', 
49900     //
49901     // private
49902  
49903     inputType : 'hidden',
49904     
49905      
49906     inputElement: false, // real input element?
49907     basedOn: false, // ????
49908     
49909     isFormField: true, // not sure where this is needed!!!!
49910
49911     onResize : function(){
49912         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49913         if(!this.boxLabel){
49914             this.el.alignTo(this.wrap, 'c-c');
49915         }
49916     },
49917
49918     initEvents : function(){
49919         Roo.form.Checkbox.superclass.initEvents.call(this);
49920         this.el.on("click", this.onClick,  this);
49921         this.el.on("change", this.onClick,  this);
49922     },
49923
49924
49925     getResizeEl : function(){
49926         return this.wrap;
49927     },
49928
49929     getPositionEl : function(){
49930         return this.wrap;
49931     },
49932
49933     
49934     // private
49935     onRender : function(ct, position){
49936         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49937        
49938         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49939         
49940         var r1 = '<table><tr>';
49941         var r2 = '<tr class="x-form-daypick-icons">';
49942         for (var i=0; i < 7; i++) {
49943             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49944             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49945         }
49946         
49947         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49948         viewEl.select('img').on('click', this.onClick, this);
49949         this.viewEl = viewEl;   
49950         
49951         
49952         // this will not work on Chrome!!!
49953         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49954         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49955         
49956         
49957           
49958
49959     },
49960
49961     // private
49962     initValue : Roo.emptyFn,
49963
49964     /**
49965      * Returns the checked state of the checkbox.
49966      * @return {Boolean} True if checked, else false
49967      */
49968     getValue : function(){
49969         return this.el.dom.value;
49970         
49971     },
49972
49973         // private
49974     onClick : function(e){ 
49975         //this.setChecked(!this.checked);
49976         Roo.get(e.target).toggleClass('x-menu-item-checked');
49977         this.refreshValue();
49978         //if(this.el.dom.checked != this.checked){
49979         //    this.setValue(this.el.dom.checked);
49980        // }
49981     },
49982     
49983     // private
49984     refreshValue : function()
49985     {
49986         var val = '';
49987         this.viewEl.select('img',true).each(function(e,i,n)  {
49988             val += e.is(".x-menu-item-checked") ? String(n) : '';
49989         });
49990         this.setValue(val, true);
49991     },
49992
49993     /**
49994      * Sets the checked state of the checkbox.
49995      * On is always based on a string comparison between inputValue and the param.
49996      * @param {Boolean/String} value - the value to set 
49997      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49998      */
49999     setValue : function(v,suppressEvent){
50000         if (!this.el.dom) {
50001             return;
50002         }
50003         var old = this.el.dom.value ;
50004         this.el.dom.value = v;
50005         if (suppressEvent) {
50006             return ;
50007         }
50008          
50009         // update display..
50010         this.viewEl.select('img',true).each(function(e,i,n)  {
50011             
50012             var on = e.is(".x-menu-item-checked");
50013             var newv = v.indexOf(String(n)) > -1;
50014             if (on != newv) {
50015                 e.toggleClass('x-menu-item-checked');
50016             }
50017             
50018         });
50019         
50020         
50021         this.fireEvent('change', this, v, old);
50022         
50023         
50024     },
50025    
50026     // handle setting of hidden value by some other method!!?!?
50027     setFromHidden: function()
50028     {
50029         if(!this.el){
50030             return;
50031         }
50032         //console.log("SET FROM HIDDEN");
50033         //alert('setFrom hidden');
50034         this.setValue(this.el.dom.value);
50035     },
50036     
50037     onDestroy : function()
50038     {
50039         if(this.viewEl){
50040             Roo.get(this.viewEl).remove();
50041         }
50042          
50043         Roo.form.DayPicker.superclass.onDestroy.call(this);
50044     }
50045
50046 });/*
50047  * RooJS Library 1.1.1
50048  * Copyright(c) 2008-2011  Alan Knowles
50049  *
50050  * License - LGPL
50051  */
50052  
50053
50054 /**
50055  * @class Roo.form.ComboCheck
50056  * @extends Roo.form.ComboBox
50057  * A combobox for multiple select items.
50058  *
50059  * FIXME - could do with a reset button..
50060  * 
50061  * @constructor
50062  * Create a new ComboCheck
50063  * @param {Object} config Configuration options
50064  */
50065 Roo.form.ComboCheck = function(config){
50066     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50067     // should verify some data...
50068     // like
50069     // hiddenName = required..
50070     // displayField = required
50071     // valudField == required
50072     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50073     var _t = this;
50074     Roo.each(req, function(e) {
50075         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50076             throw "Roo.form.ComboCheck : missing value for: " + e;
50077         }
50078     });
50079     
50080     
50081 };
50082
50083 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50084      
50085      
50086     editable : false,
50087      
50088     selectedClass: 'x-menu-item-checked', 
50089     
50090     // private
50091     onRender : function(ct, position){
50092         var _t = this;
50093         
50094         
50095         
50096         if(!this.tpl){
50097             var cls = 'x-combo-list';
50098
50099             
50100             this.tpl =  new Roo.Template({
50101                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50102                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50103                    '<span>{' + this.displayField + '}</span>' +
50104                     '</div>' 
50105                 
50106             });
50107         }
50108  
50109         
50110         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50111         this.view.singleSelect = false;
50112         this.view.multiSelect = true;
50113         this.view.toggleSelect = true;
50114         this.pageTb.add(new Roo.Toolbar.Fill(), {
50115             
50116             text: 'Done',
50117             handler: function()
50118             {
50119                 _t.collapse();
50120             }
50121         });
50122     },
50123     
50124     onViewOver : function(e, t){
50125         // do nothing...
50126         return;
50127         
50128     },
50129     
50130     onViewClick : function(doFocus,index){
50131         return;
50132         
50133     },
50134     select: function () {
50135         //Roo.log("SELECT CALLED");
50136     },
50137      
50138     selectByValue : function(xv, scrollIntoView){
50139         var ar = this.getValueArray();
50140         var sels = [];
50141         
50142         Roo.each(ar, function(v) {
50143             if(v === undefined || v === null){
50144                 return;
50145             }
50146             var r = this.findRecord(this.valueField, v);
50147             if(r){
50148                 sels.push(this.store.indexOf(r))
50149                 
50150             }
50151         },this);
50152         this.view.select(sels);
50153         return false;
50154     },
50155     
50156     
50157     
50158     onSelect : function(record, index){
50159        // Roo.log("onselect Called");
50160        // this is only called by the clear button now..
50161         this.view.clearSelections();
50162         this.setValue('[]');
50163         if (this.value != this.valueBefore) {
50164             this.fireEvent('change', this, this.value, this.valueBefore);
50165             this.valueBefore = this.value;
50166         }
50167     },
50168     getValueArray : function()
50169     {
50170         var ar = [] ;
50171         
50172         try {
50173             //Roo.log(this.value);
50174             if (typeof(this.value) == 'undefined') {
50175                 return [];
50176             }
50177             var ar = Roo.decode(this.value);
50178             return  ar instanceof Array ? ar : []; //?? valid?
50179             
50180         } catch(e) {
50181             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50182             return [];
50183         }
50184          
50185     },
50186     expand : function ()
50187     {
50188         
50189         Roo.form.ComboCheck.superclass.expand.call(this);
50190         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50191         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50192         
50193
50194     },
50195     
50196     collapse : function(){
50197         Roo.form.ComboCheck.superclass.collapse.call(this);
50198         var sl = this.view.getSelectedIndexes();
50199         var st = this.store;
50200         var nv = [];
50201         var tv = [];
50202         var r;
50203         Roo.each(sl, function(i) {
50204             r = st.getAt(i);
50205             nv.push(r.get(this.valueField));
50206         },this);
50207         this.setValue(Roo.encode(nv));
50208         if (this.value != this.valueBefore) {
50209
50210             this.fireEvent('change', this, this.value, this.valueBefore);
50211             this.valueBefore = this.value;
50212         }
50213         
50214     },
50215     
50216     setValue : function(v){
50217         // Roo.log(v);
50218         this.value = v;
50219         
50220         var vals = this.getValueArray();
50221         var tv = [];
50222         Roo.each(vals, function(k) {
50223             var r = this.findRecord(this.valueField, k);
50224             if(r){
50225                 tv.push(r.data[this.displayField]);
50226             }else if(this.valueNotFoundText !== undefined){
50227                 tv.push( this.valueNotFoundText );
50228             }
50229         },this);
50230        // Roo.log(tv);
50231         
50232         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50233         this.hiddenField.value = v;
50234         this.value = v;
50235     }
50236     
50237 });/*
50238  * Based on:
50239  * Ext JS Library 1.1.1
50240  * Copyright(c) 2006-2007, Ext JS, LLC.
50241  *
50242  * Originally Released Under LGPL - original licence link has changed is not relivant.
50243  *
50244  * Fork - LGPL
50245  * <script type="text/javascript">
50246  */
50247  
50248 /**
50249  * @class Roo.form.Signature
50250  * @extends Roo.form.Field
50251  * Signature field.  
50252  * @constructor
50253  * 
50254  * @param {Object} config Configuration options
50255  */
50256
50257 Roo.form.Signature = function(config){
50258     Roo.form.Signature.superclass.constructor.call(this, config);
50259     
50260     this.addEvents({// not in used??
50261          /**
50262          * @event confirm
50263          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50264              * @param {Roo.form.Signature} combo This combo box
50265              */
50266         'confirm' : true,
50267         /**
50268          * @event reset
50269          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50270              * @param {Roo.form.ComboBox} combo This combo box
50271              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50272              */
50273         'reset' : true
50274     });
50275 };
50276
50277 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50278     /**
50279      * @cfg {Object} labels Label to use when rendering a form.
50280      * defaults to 
50281      * labels : { 
50282      *      clear : "Clear",
50283      *      confirm : "Confirm"
50284      *  }
50285      */
50286     labels : { 
50287         clear : "Clear",
50288         confirm : "Confirm"
50289     },
50290     /**
50291      * @cfg {Number} width The signature panel width (defaults to 300)
50292      */
50293     width: 300,
50294     /**
50295      * @cfg {Number} height The signature panel height (defaults to 100)
50296      */
50297     height : 100,
50298     /**
50299      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50300      */
50301     allowBlank : false,
50302     
50303     //private
50304     // {Object} signPanel The signature SVG panel element (defaults to {})
50305     signPanel : {},
50306     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50307     isMouseDown : false,
50308     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50309     isConfirmed : false,
50310     // {String} signatureTmp SVG mapping string (defaults to empty string)
50311     signatureTmp : '',
50312     
50313     
50314     defaultAutoCreate : { // modified by initCompnoent..
50315         tag: "input",
50316         type:"hidden"
50317     },
50318
50319     // private
50320     onRender : function(ct, position){
50321         
50322         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50323         
50324         this.wrap = this.el.wrap({
50325             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50326         });
50327         
50328         this.createToolbar(this);
50329         this.signPanel = this.wrap.createChild({
50330                 tag: 'div',
50331                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50332             }, this.el
50333         );
50334             
50335         this.svgID = Roo.id();
50336         this.svgEl = this.signPanel.createChild({
50337               xmlns : 'http://www.w3.org/2000/svg',
50338               tag : 'svg',
50339               id : this.svgID + "-svg",
50340               width: this.width,
50341               height: this.height,
50342               viewBox: '0 0 '+this.width+' '+this.height,
50343               cn : [
50344                 {
50345                     tag: "rect",
50346                     id: this.svgID + "-svg-r",
50347                     width: this.width,
50348                     height: this.height,
50349                     fill: "#ffa"
50350                 },
50351                 {
50352                     tag: "line",
50353                     id: this.svgID + "-svg-l",
50354                     x1: "0", // start
50355                     y1: (this.height*0.8), // start set the line in 80% of height
50356                     x2: this.width, // end
50357                     y2: (this.height*0.8), // end set the line in 80% of height
50358                     'stroke': "#666",
50359                     'stroke-width': "1",
50360                     'stroke-dasharray': "3",
50361                     'shape-rendering': "crispEdges",
50362                     'pointer-events': "none"
50363                 },
50364                 {
50365                     tag: "path",
50366                     id: this.svgID + "-svg-p",
50367                     'stroke': "navy",
50368                     'stroke-width': "3",
50369                     'fill': "none",
50370                     'pointer-events': 'none'
50371                 }
50372               ]
50373         });
50374         this.createSVG();
50375         this.svgBox = this.svgEl.dom.getScreenCTM();
50376     },
50377     createSVG : function(){ 
50378         var svg = this.signPanel;
50379         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50380         var t = this;
50381
50382         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50383         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50384         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50385         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50386         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50387         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50388         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50389         
50390     },
50391     isTouchEvent : function(e){
50392         return e.type.match(/^touch/);
50393     },
50394     getCoords : function (e) {
50395         var pt    = this.svgEl.dom.createSVGPoint();
50396         pt.x = e.clientX; 
50397         pt.y = e.clientY;
50398         if (this.isTouchEvent(e)) {
50399             pt.x =  e.targetTouches[0].clientX;
50400             pt.y = e.targetTouches[0].clientY;
50401         }
50402         var a = this.svgEl.dom.getScreenCTM();
50403         var b = a.inverse();
50404         var mx = pt.matrixTransform(b);
50405         return mx.x + ',' + mx.y;
50406     },
50407     //mouse event headler 
50408     down : function (e) {
50409         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50410         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50411         
50412         this.isMouseDown = true;
50413         
50414         e.preventDefault();
50415     },
50416     move : function (e) {
50417         if (this.isMouseDown) {
50418             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50419             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50420         }
50421         
50422         e.preventDefault();
50423     },
50424     up : function (e) {
50425         this.isMouseDown = false;
50426         var sp = this.signatureTmp.split(' ');
50427         
50428         if(sp.length > 1){
50429             if(!sp[sp.length-2].match(/^L/)){
50430                 sp.pop();
50431                 sp.pop();
50432                 sp.push("");
50433                 this.signatureTmp = sp.join(" ");
50434             }
50435         }
50436         if(this.getValue() != this.signatureTmp){
50437             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50438             this.isConfirmed = false;
50439         }
50440         e.preventDefault();
50441     },
50442     
50443     /**
50444      * Protected method that will not generally be called directly. It
50445      * is called when the editor creates its toolbar. Override this method if you need to
50446      * add custom toolbar buttons.
50447      * @param {HtmlEditor} editor
50448      */
50449     createToolbar : function(editor){
50450          function btn(id, toggle, handler){
50451             var xid = fid + '-'+ id ;
50452             return {
50453                 id : xid,
50454                 cmd : id,
50455                 cls : 'x-btn-icon x-edit-'+id,
50456                 enableToggle:toggle !== false,
50457                 scope: editor, // was editor...
50458                 handler:handler||editor.relayBtnCmd,
50459                 clickEvent:'mousedown',
50460                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50461                 tabIndex:-1
50462             };
50463         }
50464         
50465         
50466         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50467         this.tb = tb;
50468         this.tb.add(
50469            {
50470                 cls : ' x-signature-btn x-signature-'+id,
50471                 scope: editor, // was editor...
50472                 handler: this.reset,
50473                 clickEvent:'mousedown',
50474                 text: this.labels.clear
50475             },
50476             {
50477                  xtype : 'Fill',
50478                  xns: Roo.Toolbar
50479             }, 
50480             {
50481                 cls : '  x-signature-btn x-signature-'+id,
50482                 scope: editor, // was editor...
50483                 handler: this.confirmHandler,
50484                 clickEvent:'mousedown',
50485                 text: this.labels.confirm
50486             }
50487         );
50488     
50489     },
50490     //public
50491     /**
50492      * when user is clicked confirm then show this image.....
50493      * 
50494      * @return {String} Image Data URI
50495      */
50496     getImageDataURI : function(){
50497         var svg = this.svgEl.dom.parentNode.innerHTML;
50498         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50499         return src; 
50500     },
50501     /**
50502      * 
50503      * @return {Boolean} this.isConfirmed
50504      */
50505     getConfirmed : function(){
50506         return this.isConfirmed;
50507     },
50508     /**
50509      * 
50510      * @return {Number} this.width
50511      */
50512     getWidth : function(){
50513         return this.width;
50514     },
50515     /**
50516      * 
50517      * @return {Number} this.height
50518      */
50519     getHeight : function(){
50520         return this.height;
50521     },
50522     // private
50523     getSignature : function(){
50524         return this.signatureTmp;
50525     },
50526     // private
50527     reset : function(){
50528         this.signatureTmp = '';
50529         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50530         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50531         this.isConfirmed = false;
50532         Roo.form.Signature.superclass.reset.call(this);
50533     },
50534     setSignature : function(s){
50535         this.signatureTmp = s;
50536         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50537         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50538         this.setValue(s);
50539         this.isConfirmed = false;
50540         Roo.form.Signature.superclass.reset.call(this);
50541     }, 
50542     test : function(){
50543 //        Roo.log(this.signPanel.dom.contentWindow.up())
50544     },
50545     //private
50546     setConfirmed : function(){
50547         
50548         
50549         
50550 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50551     },
50552     // private
50553     confirmHandler : function(){
50554         if(!this.getSignature()){
50555             return;
50556         }
50557         
50558         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50559         this.setValue(this.getSignature());
50560         this.isConfirmed = true;
50561         
50562         this.fireEvent('confirm', this);
50563     },
50564     // private
50565     // Subclasses should provide the validation implementation by overriding this
50566     validateValue : function(value){
50567         if(this.allowBlank){
50568             return true;
50569         }
50570         
50571         if(this.isConfirmed){
50572             return true;
50573         }
50574         return false;
50575     }
50576 });/*
50577  * Based on:
50578  * Ext JS Library 1.1.1
50579  * Copyright(c) 2006-2007, Ext JS, LLC.
50580  *
50581  * Originally Released Under LGPL - original licence link has changed is not relivant.
50582  *
50583  * Fork - LGPL
50584  * <script type="text/javascript">
50585  */
50586  
50587
50588 /**
50589  * @class Roo.form.ComboBox
50590  * @extends Roo.form.TriggerField
50591  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
50592  * @constructor
50593  * Create a new ComboBox.
50594  * @param {Object} config Configuration options
50595  */
50596 Roo.form.Select = function(config){
50597     Roo.form.Select.superclass.constructor.call(this, config);
50598      
50599 };
50600
50601 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
50602     /**
50603      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
50604      */
50605     /**
50606      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
50607      * rendering into an Roo.Editor, defaults to false)
50608      */
50609     /**
50610      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
50611      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
50612      */
50613     /**
50614      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
50615      */
50616     /**
50617      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
50618      * the dropdown list (defaults to undefined, with no header element)
50619      */
50620
50621      /**
50622      * @cfg {String/Roo.Template} tpl The template to use to render the output
50623      */
50624      
50625     // private
50626     defaultAutoCreate : {tag: "select"  },
50627     /**
50628      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
50629      */
50630     listWidth: undefined,
50631     /**
50632      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
50633      * mode = 'remote' or 'text' if mode = 'local')
50634      */
50635     displayField: undefined,
50636     /**
50637      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
50638      * mode = 'remote' or 'value' if mode = 'local'). 
50639      * Note: use of a valueField requires the user make a selection
50640      * in order for a value to be mapped.
50641      */
50642     valueField: undefined,
50643     
50644     
50645     /**
50646      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
50647      * field's data value (defaults to the underlying DOM element's name)
50648      */
50649     hiddenName: undefined,
50650     /**
50651      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
50652      */
50653     listClass: '',
50654     /**
50655      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
50656      */
50657     selectedClass: 'x-combo-selected',
50658     /**
50659      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
50660      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
50661      * which displays a downward arrow icon).
50662      */
50663     triggerClass : 'x-form-arrow-trigger',
50664     /**
50665      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
50666      */
50667     shadow:'sides',
50668     /**
50669      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
50670      * anchor positions (defaults to 'tl-bl')
50671      */
50672     listAlign: 'tl-bl?',
50673     /**
50674      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
50675      */
50676     maxHeight: 300,
50677     /**
50678      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
50679      * query specified by the allQuery config option (defaults to 'query')
50680      */
50681     triggerAction: 'query',
50682     /**
50683      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
50684      * (defaults to 4, does not apply if editable = false)
50685      */
50686     minChars : 4,
50687     /**
50688      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
50689      * delay (typeAheadDelay) if it matches a known value (defaults to false)
50690      */
50691     typeAhead: false,
50692     /**
50693      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
50694      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
50695      */
50696     queryDelay: 500,
50697     /**
50698      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
50699      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
50700      */
50701     pageSize: 0,
50702     /**
50703      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
50704      * when editable = true (defaults to false)
50705      */
50706     selectOnFocus:false,
50707     /**
50708      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
50709      */
50710     queryParam: 'query',
50711     /**
50712      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
50713      * when mode = 'remote' (defaults to 'Loading...')
50714      */
50715     loadingText: 'Loading...',
50716     /**
50717      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
50718      */
50719     resizable: false,
50720     /**
50721      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
50722      */
50723     handleHeight : 8,
50724     /**
50725      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
50726      * traditional select (defaults to true)
50727      */
50728     editable: true,
50729     /**
50730      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
50731      */
50732     allQuery: '',
50733     /**
50734      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
50735      */
50736     mode: 'remote',
50737     /**
50738      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
50739      * listWidth has a higher value)
50740      */
50741     minListWidth : 70,
50742     /**
50743      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
50744      * allow the user to set arbitrary text into the field (defaults to false)
50745      */
50746     forceSelection:false,
50747     /**
50748      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
50749      * if typeAhead = true (defaults to 250)
50750      */
50751     typeAheadDelay : 250,
50752     /**
50753      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
50754      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
50755      */
50756     valueNotFoundText : undefined,
50757     
50758     /**
50759      * @cfg {String} defaultValue The value displayed after loading the store.
50760      */
50761     defaultValue: '',
50762     
50763     /**
50764      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
50765      */
50766     blockFocus : false,
50767     
50768     /**
50769      * @cfg {Boolean} disableClear Disable showing of clear button.
50770      */
50771     disableClear : false,
50772     /**
50773      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
50774      */
50775     alwaysQuery : false,
50776     
50777     //private
50778     addicon : false,
50779     editicon: false,
50780     
50781     // element that contains real text value.. (when hidden is used..)
50782      
50783     // private
50784     onRender : function(ct, position){
50785         Roo.form.Field.prototype.onRender.call(this, ct, position);
50786         
50787         if(this.store){
50788             this.store.on('beforeload', this.onBeforeLoad, this);
50789             this.store.on('load', this.onLoad, this);
50790             this.store.on('loadexception', this.onLoadException, this);
50791             this.store.load({});
50792         }
50793         
50794         
50795         
50796     },
50797
50798     // private
50799     initEvents : function(){
50800         //Roo.form.ComboBox.superclass.initEvents.call(this);
50801  
50802     },
50803
50804     onDestroy : function(){
50805        
50806         if(this.store){
50807             this.store.un('beforeload', this.onBeforeLoad, this);
50808             this.store.un('load', this.onLoad, this);
50809             this.store.un('loadexception', this.onLoadException, this);
50810         }
50811         //Roo.form.ComboBox.superclass.onDestroy.call(this);
50812     },
50813
50814     // private
50815     fireKey : function(e){
50816         if(e.isNavKeyPress() && !this.list.isVisible()){
50817             this.fireEvent("specialkey", this, e);
50818         }
50819     },
50820
50821     // private
50822     onResize: function(w, h){
50823         
50824         return; 
50825     
50826         
50827     },
50828
50829     /**
50830      * Allow or prevent the user from directly editing the field text.  If false is passed,
50831      * the user will only be able to select from the items defined in the dropdown list.  This method
50832      * is the runtime equivalent of setting the 'editable' config option at config time.
50833      * @param {Boolean} value True to allow the user to directly edit the field text
50834      */
50835     setEditable : function(value){
50836          
50837     },
50838
50839     // private
50840     onBeforeLoad : function(){
50841         
50842         Roo.log("Select before load");
50843         return;
50844     
50845         this.innerList.update(this.loadingText ?
50846                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
50847         //this.restrictHeight();
50848         this.selectedIndex = -1;
50849     },
50850
50851     // private
50852     onLoad : function(){
50853
50854     
50855         var dom = this.el.dom;
50856         dom.innerHTML = '';
50857          var od = dom.ownerDocument;
50858          
50859         if (this.emptyText) {
50860             var op = od.createElement('option');
50861             op.setAttribute('value', '');
50862             op.innerHTML = String.format('{0}', this.emptyText);
50863             dom.appendChild(op);
50864         }
50865         if(this.store.getCount() > 0){
50866            
50867             var vf = this.valueField;
50868             var df = this.displayField;
50869             this.store.data.each(function(r) {
50870                 // which colmsn to use... testing - cdoe / title..
50871                 var op = od.createElement('option');
50872                 op.setAttribute('value', r.data[vf]);
50873                 op.innerHTML = String.format('{0}', r.data[df]);
50874                 dom.appendChild(op);
50875             });
50876             if (typeof(this.defaultValue != 'undefined')) {
50877                 this.setValue(this.defaultValue);
50878             }
50879             
50880              
50881         }else{
50882             //this.onEmptyResults();
50883         }
50884         //this.el.focus();
50885     },
50886     // private
50887     onLoadException : function()
50888     {
50889         dom.innerHTML = '';
50890             
50891         Roo.log("Select on load exception");
50892         return;
50893     
50894         this.collapse();
50895         Roo.log(this.store.reader.jsonData);
50896         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50897             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50898         }
50899         
50900         
50901     },
50902     // private
50903     onTypeAhead : function(){
50904          
50905     },
50906
50907     // private
50908     onSelect : function(record, index){
50909         Roo.log('on select?');
50910         return;
50911         if(this.fireEvent('beforeselect', this, record, index) !== false){
50912             this.setFromData(index > -1 ? record.data : false);
50913             this.collapse();
50914             this.fireEvent('select', this, record, index);
50915         }
50916     },
50917
50918     /**
50919      * Returns the currently selected field value or empty string if no value is set.
50920      * @return {String} value The selected value
50921      */
50922     getValue : function(){
50923         var dom = this.el.dom;
50924         this.value = dom.options[dom.selectedIndex].value;
50925         return this.value;
50926         
50927     },
50928
50929     /**
50930      * Clears any text/value currently set in the field
50931      */
50932     clearValue : function(){
50933         this.value = '';
50934         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50935         
50936     },
50937
50938     /**
50939      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50940      * will be displayed in the field.  If the value does not match the data value of an existing item,
50941      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50942      * Otherwise the field will be blank (although the value will still be set).
50943      * @param {String} value The value to match
50944      */
50945     setValue : function(v){
50946         var d = this.el.dom;
50947         for (var i =0; i < d.options.length;i++) {
50948             if (v == d.options[i].value) {
50949                 d.selectedIndex = i;
50950                 this.value = v;
50951                 return;
50952             }
50953         }
50954         this.clearValue();
50955     },
50956     /**
50957      * @property {Object} the last set data for the element
50958      */
50959     
50960     lastData : false,
50961     /**
50962      * Sets the value of the field based on a object which is related to the record format for the store.
50963      * @param {Object} value the value to set as. or false on reset?
50964      */
50965     setFromData : function(o){
50966         Roo.log('setfrom data?');
50967          
50968         
50969         
50970     },
50971     // private
50972     reset : function(){
50973         this.clearValue();
50974     },
50975     // private
50976     findRecord : function(prop, value){
50977         
50978         return false;
50979     
50980         var record;
50981         if(this.store.getCount() > 0){
50982             this.store.each(function(r){
50983                 if(r.data[prop] == value){
50984                     record = r;
50985                     return false;
50986                 }
50987                 return true;
50988             });
50989         }
50990         return record;
50991     },
50992     
50993     getName: function()
50994     {
50995         // returns hidden if it's set..
50996         if (!this.rendered) {return ''};
50997         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50998         
50999     },
51000      
51001
51002     
51003
51004     // private
51005     onEmptyResults : function(){
51006         Roo.log('empty results');
51007         //this.collapse();
51008     },
51009
51010     /**
51011      * Returns true if the dropdown list is expanded, else false.
51012      */
51013     isExpanded : function(){
51014         return false;
51015     },
51016
51017     /**
51018      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51019      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51020      * @param {String} value The data value of the item to select
51021      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51022      * selected item if it is not currently in view (defaults to true)
51023      * @return {Boolean} True if the value matched an item in the list, else false
51024      */
51025     selectByValue : function(v, scrollIntoView){
51026         Roo.log('select By Value');
51027         return false;
51028     
51029         if(v !== undefined && v !== null){
51030             var r = this.findRecord(this.valueField || this.displayField, v);
51031             if(r){
51032                 this.select(this.store.indexOf(r), scrollIntoView);
51033                 return true;
51034             }
51035         }
51036         return false;
51037     },
51038
51039     /**
51040      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51041      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51042      * @param {Number} index The zero-based index of the list item to select
51043      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51044      * selected item if it is not currently in view (defaults to true)
51045      */
51046     select : function(index, scrollIntoView){
51047         Roo.log('select ');
51048         return  ;
51049         
51050         this.selectedIndex = index;
51051         this.view.select(index);
51052         if(scrollIntoView !== false){
51053             var el = this.view.getNode(index);
51054             if(el){
51055                 this.innerList.scrollChildIntoView(el, false);
51056             }
51057         }
51058     },
51059
51060       
51061
51062     // private
51063     validateBlur : function(){
51064         
51065         return;
51066         
51067     },
51068
51069     // private
51070     initQuery : function(){
51071         this.doQuery(this.getRawValue());
51072     },
51073
51074     // private
51075     doForce : function(){
51076         if(this.el.dom.value.length > 0){
51077             this.el.dom.value =
51078                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51079              
51080         }
51081     },
51082
51083     /**
51084      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51085      * query allowing the query action to be canceled if needed.
51086      * @param {String} query The SQL query to execute
51087      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51088      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51089      * saved in the current store (defaults to false)
51090      */
51091     doQuery : function(q, forceAll){
51092         
51093         Roo.log('doQuery?');
51094         if(q === undefined || q === null){
51095             q = '';
51096         }
51097         var qe = {
51098             query: q,
51099             forceAll: forceAll,
51100             combo: this,
51101             cancel:false
51102         };
51103         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51104             return false;
51105         }
51106         q = qe.query;
51107         forceAll = qe.forceAll;
51108         if(forceAll === true || (q.length >= this.minChars)){
51109             if(this.lastQuery != q || this.alwaysQuery){
51110                 this.lastQuery = q;
51111                 if(this.mode == 'local'){
51112                     this.selectedIndex = -1;
51113                     if(forceAll){
51114                         this.store.clearFilter();
51115                     }else{
51116                         this.store.filter(this.displayField, q);
51117                     }
51118                     this.onLoad();
51119                 }else{
51120                     this.store.baseParams[this.queryParam] = q;
51121                     this.store.load({
51122                         params: this.getParams(q)
51123                     });
51124                     this.expand();
51125                 }
51126             }else{
51127                 this.selectedIndex = -1;
51128                 this.onLoad();   
51129             }
51130         }
51131     },
51132
51133     // private
51134     getParams : function(q){
51135         var p = {};
51136         //p[this.queryParam] = q;
51137         if(this.pageSize){
51138             p.start = 0;
51139             p.limit = this.pageSize;
51140         }
51141         return p;
51142     },
51143
51144     /**
51145      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51146      */
51147     collapse : function(){
51148         
51149     },
51150
51151     // private
51152     collapseIf : function(e){
51153         
51154     },
51155
51156     /**
51157      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51158      */
51159     expand : function(){
51160         
51161     } ,
51162
51163     // private
51164      
51165
51166     /** 
51167     * @cfg {Boolean} grow 
51168     * @hide 
51169     */
51170     /** 
51171     * @cfg {Number} growMin 
51172     * @hide 
51173     */
51174     /** 
51175     * @cfg {Number} growMax 
51176     * @hide 
51177     */
51178     /**
51179      * @hide
51180      * @method autoSize
51181      */
51182     
51183     setWidth : function()
51184     {
51185         
51186     },
51187     getResizeEl : function(){
51188         return this.el;
51189     }
51190 });//<script type="text/javasscript">
51191  
51192
51193 /**
51194  * @class Roo.DDView
51195  * A DnD enabled version of Roo.View.
51196  * @param {Element/String} container The Element in which to create the View.
51197  * @param {String} tpl The template string used to create the markup for each element of the View
51198  * @param {Object} config The configuration properties. These include all the config options of
51199  * {@link Roo.View} plus some specific to this class.<br>
51200  * <p>
51201  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51202  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51203  * <p>
51204  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51205 .x-view-drag-insert-above {
51206         border-top:1px dotted #3366cc;
51207 }
51208 .x-view-drag-insert-below {
51209         border-bottom:1px dotted #3366cc;
51210 }
51211 </code></pre>
51212  * 
51213  */
51214  
51215 Roo.DDView = function(container, tpl, config) {
51216     Roo.DDView.superclass.constructor.apply(this, arguments);
51217     this.getEl().setStyle("outline", "0px none");
51218     this.getEl().unselectable();
51219     if (this.dragGroup) {
51220                 this.setDraggable(this.dragGroup.split(","));
51221     }
51222     if (this.dropGroup) {
51223                 this.setDroppable(this.dropGroup.split(","));
51224     }
51225     if (this.deletable) {
51226         this.setDeletable();
51227     }
51228     this.isDirtyFlag = false;
51229         this.addEvents({
51230                 "drop" : true
51231         });
51232 };
51233
51234 Roo.extend(Roo.DDView, Roo.View, {
51235 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51236 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51237 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51238 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51239
51240         isFormField: true,
51241
51242         reset: Roo.emptyFn,
51243         
51244         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51245
51246         validate: function() {
51247                 return true;
51248         },
51249         
51250         destroy: function() {
51251                 this.purgeListeners();
51252                 this.getEl.removeAllListeners();
51253                 this.getEl().remove();
51254                 if (this.dragZone) {
51255                         if (this.dragZone.destroy) {
51256                                 this.dragZone.destroy();
51257                         }
51258                 }
51259                 if (this.dropZone) {
51260                         if (this.dropZone.destroy) {
51261                                 this.dropZone.destroy();
51262                         }
51263                 }
51264         },
51265
51266 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51267         getName: function() {
51268                 return this.name;
51269         },
51270
51271 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51272         setValue: function(v) {
51273                 if (!this.store) {
51274                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51275                 }
51276                 var data = {};
51277                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51278                 this.store.proxy = new Roo.data.MemoryProxy(data);
51279                 this.store.load();
51280         },
51281
51282 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51283         getValue: function() {
51284                 var result = '(';
51285                 this.store.each(function(rec) {
51286                         result += rec.id + ',';
51287                 });
51288                 return result.substr(0, result.length - 1) + ')';
51289         },
51290         
51291         getIds: function() {
51292                 var i = 0, result = new Array(this.store.getCount());
51293                 this.store.each(function(rec) {
51294                         result[i++] = rec.id;
51295                 });
51296                 return result;
51297         },
51298         
51299         isDirty: function() {
51300                 return this.isDirtyFlag;
51301         },
51302
51303 /**
51304  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51305  *      whole Element becomes the target, and this causes the drop gesture to append.
51306  */
51307     getTargetFromEvent : function(e) {
51308                 var target = e.getTarget();
51309                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51310                 target = target.parentNode;
51311                 }
51312                 if (!target) {
51313                         target = this.el.dom.lastChild || this.el.dom;
51314                 }
51315                 return target;
51316     },
51317
51318 /**
51319  *      Create the drag data which consists of an object which has the property "ddel" as
51320  *      the drag proxy element. 
51321  */
51322     getDragData : function(e) {
51323         var target = this.findItemFromChild(e.getTarget());
51324                 if(target) {
51325                         this.handleSelection(e);
51326                         var selNodes = this.getSelectedNodes();
51327             var dragData = {
51328                 source: this,
51329                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51330                 nodes: selNodes,
51331                 records: []
51332                         };
51333                         var selectedIndices = this.getSelectedIndexes();
51334                         for (var i = 0; i < selectedIndices.length; i++) {
51335                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51336                         }
51337                         if (selNodes.length == 1) {
51338                                 dragData.ddel = target.cloneNode(true); // the div element
51339                         } else {
51340                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51341                                 div.className = 'multi-proxy';
51342                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51343                                         div.appendChild(selNodes[i].cloneNode(true));
51344                                 }
51345                                 dragData.ddel = div;
51346                         }
51347             //console.log(dragData)
51348             //console.log(dragData.ddel.innerHTML)
51349                         return dragData;
51350                 }
51351         //console.log('nodragData')
51352                 return false;
51353     },
51354     
51355 /**     Specify to which ddGroup items in this DDView may be dragged. */
51356     setDraggable: function(ddGroup) {
51357         if (ddGroup instanceof Array) {
51358                 Roo.each(ddGroup, this.setDraggable, this);
51359                 return;
51360         }
51361         if (this.dragZone) {
51362                 this.dragZone.addToGroup(ddGroup);
51363         } else {
51364                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51365                                 containerScroll: true,
51366                                 ddGroup: ddGroup 
51367
51368                         });
51369 //                      Draggability implies selection. DragZone's mousedown selects the element.
51370                         if (!this.multiSelect) { this.singleSelect = true; }
51371
51372 //                      Wire the DragZone's handlers up to methods in *this*
51373                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51374                 }
51375     },
51376
51377 /**     Specify from which ddGroup this DDView accepts drops. */
51378     setDroppable: function(ddGroup) {
51379         if (ddGroup instanceof Array) {
51380                 Roo.each(ddGroup, this.setDroppable, this);
51381                 return;
51382         }
51383         if (this.dropZone) {
51384                 this.dropZone.addToGroup(ddGroup);
51385         } else {
51386                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51387                                 containerScroll: true,
51388                                 ddGroup: ddGroup
51389                         });
51390
51391 //                      Wire the DropZone's handlers up to methods in *this*
51392                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51393                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51394                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51395                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51396                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51397                 }
51398     },
51399
51400 /**     Decide whether to drop above or below a View node. */
51401     getDropPoint : function(e, n, dd){
51402         if (n == this.el.dom) { return "above"; }
51403                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51404                 var c = t + (b - t) / 2;
51405                 var y = Roo.lib.Event.getPageY(e);
51406                 if(y <= c) {
51407                         return "above";
51408                 }else{
51409                         return "below";
51410                 }
51411     },
51412
51413     onNodeEnter : function(n, dd, e, data){
51414                 return false;
51415     },
51416     
51417     onNodeOver : function(n, dd, e, data){
51418                 var pt = this.getDropPoint(e, n, dd);
51419                 // set the insert point style on the target node
51420                 var dragElClass = this.dropNotAllowed;
51421                 if (pt) {
51422                         var targetElClass;
51423                         if (pt == "above"){
51424                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51425                                 targetElClass = "x-view-drag-insert-above";
51426                         } else {
51427                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51428                                 targetElClass = "x-view-drag-insert-below";
51429                         }
51430                         if (this.lastInsertClass != targetElClass){
51431                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51432                                 this.lastInsertClass = targetElClass;
51433                         }
51434                 }
51435                 return dragElClass;
51436         },
51437
51438     onNodeOut : function(n, dd, e, data){
51439                 this.removeDropIndicators(n);
51440     },
51441
51442     onNodeDrop : function(n, dd, e, data){
51443         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51444                 return false;
51445         }
51446         var pt = this.getDropPoint(e, n, dd);
51447                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51448                 if (pt == "below") { insertAt++; }
51449                 for (var i = 0; i < data.records.length; i++) {
51450                         var r = data.records[i];
51451                         var dup = this.store.getById(r.id);
51452                         if (dup && (dd != this.dragZone)) {
51453                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51454                         } else {
51455                                 if (data.copy) {
51456                                         this.store.insert(insertAt++, r.copy());
51457                                 } else {
51458                                         data.source.isDirtyFlag = true;
51459                                         r.store.remove(r);
51460                                         this.store.insert(insertAt++, r);
51461                                 }
51462                                 this.isDirtyFlag = true;
51463                         }
51464                 }
51465                 this.dragZone.cachedTarget = null;
51466                 return true;
51467     },
51468
51469     removeDropIndicators : function(n){
51470                 if(n){
51471                         Roo.fly(n).removeClass([
51472                                 "x-view-drag-insert-above",
51473                                 "x-view-drag-insert-below"]);
51474                         this.lastInsertClass = "_noclass";
51475                 }
51476     },
51477
51478 /**
51479  *      Utility method. Add a delete option to the DDView's context menu.
51480  *      @param {String} imageUrl The URL of the "delete" icon image.
51481  */
51482         setDeletable: function(imageUrl) {
51483                 if (!this.singleSelect && !this.multiSelect) {
51484                         this.singleSelect = true;
51485                 }
51486                 var c = this.getContextMenu();
51487                 this.contextMenu.on("itemclick", function(item) {
51488                         switch (item.id) {
51489                                 case "delete":
51490                                         this.remove(this.getSelectedIndexes());
51491                                         break;
51492                         }
51493                 }, this);
51494                 this.contextMenu.add({
51495                         icon: imageUrl,
51496                         id: "delete",
51497                         text: 'Delete'
51498                 });
51499         },
51500         
51501 /**     Return the context menu for this DDView. */
51502         getContextMenu: function() {
51503                 if (!this.contextMenu) {
51504 //                      Create the View's context menu
51505                         this.contextMenu = new Roo.menu.Menu({
51506                                 id: this.id + "-contextmenu"
51507                         });
51508                         this.el.on("contextmenu", this.showContextMenu, this);
51509                 }
51510                 return this.contextMenu;
51511         },
51512         
51513         disableContextMenu: function() {
51514                 if (this.contextMenu) {
51515                         this.el.un("contextmenu", this.showContextMenu, this);
51516                 }
51517         },
51518
51519         showContextMenu: function(e, item) {
51520         item = this.findItemFromChild(e.getTarget());
51521                 if (item) {
51522                         e.stopEvent();
51523                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51524                         this.contextMenu.showAt(e.getXY());
51525             }
51526     },
51527
51528 /**
51529  *      Remove {@link Roo.data.Record}s at the specified indices.
51530  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51531  */
51532     remove: function(selectedIndices) {
51533                 selectedIndices = [].concat(selectedIndices);
51534                 for (var i = 0; i < selectedIndices.length; i++) {
51535                         var rec = this.store.getAt(selectedIndices[i]);
51536                         this.store.remove(rec);
51537                 }
51538     },
51539
51540 /**
51541  *      Double click fires the event, but also, if this is draggable, and there is only one other
51542  *      related DropZone, it transfers the selected node.
51543  */
51544     onDblClick : function(e){
51545         var item = this.findItemFromChild(e.getTarget());
51546         if(item){
51547             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51548                 return false;
51549             }
51550             if (this.dragGroup) {
51551                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51552                     while (targets.indexOf(this.dropZone) > -1) {
51553                             targets.remove(this.dropZone);
51554                                 }
51555                     if (targets.length == 1) {
51556                                         this.dragZone.cachedTarget = null;
51557                         var el = Roo.get(targets[0].getEl());
51558                         var box = el.getBox(true);
51559                         targets[0].onNodeDrop(el.dom, {
51560                                 target: el.dom,
51561                                 xy: [box.x, box.y + box.height - 1]
51562                         }, null, this.getDragData(e));
51563                     }
51564                 }
51565         }
51566     },
51567     
51568     handleSelection: function(e) {
51569                 this.dragZone.cachedTarget = null;
51570         var item = this.findItemFromChild(e.getTarget());
51571         if (!item) {
51572                 this.clearSelections(true);
51573                 return;
51574         }
51575                 if (item && (this.multiSelect || this.singleSelect)){
51576                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51577                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51578                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51579                                 this.unselect(item);
51580                         } else {
51581                                 this.select(item, this.multiSelect && e.ctrlKey);
51582                                 this.lastSelection = item;
51583                         }
51584                 }
51585     },
51586
51587     onItemClick : function(item, index, e){
51588                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
51589                         return false;
51590                 }
51591                 return true;
51592     },
51593
51594     unselect : function(nodeInfo, suppressEvent){
51595                 var node = this.getNode(nodeInfo);
51596                 if(node && this.isSelected(node)){
51597                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
51598                                 Roo.fly(node).removeClass(this.selectedClass);
51599                                 this.selections.remove(node);
51600                                 if(!suppressEvent){
51601                                         this.fireEvent("selectionchange", this, this.selections);
51602                                 }
51603                         }
51604                 }
51605     }
51606 });
51607 /*
51608  * Based on:
51609  * Ext JS Library 1.1.1
51610  * Copyright(c) 2006-2007, Ext JS, LLC.
51611  *
51612  * Originally Released Under LGPL - original licence link has changed is not relivant.
51613  *
51614  * Fork - LGPL
51615  * <script type="text/javascript">
51616  */
51617  
51618 /**
51619  * @class Roo.LayoutManager
51620  * @extends Roo.util.Observable
51621  * Base class for layout managers.
51622  */
51623 Roo.LayoutManager = function(container, config){
51624     Roo.LayoutManager.superclass.constructor.call(this);
51625     this.el = Roo.get(container);
51626     // ie scrollbar fix
51627     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
51628         document.body.scroll = "no";
51629     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
51630         this.el.position('relative');
51631     }
51632     this.id = this.el.id;
51633     this.el.addClass("x-layout-container");
51634     /** false to disable window resize monitoring @type Boolean */
51635     this.monitorWindowResize = true;
51636     this.regions = {};
51637     this.addEvents({
51638         /**
51639          * @event layout
51640          * Fires when a layout is performed. 
51641          * @param {Roo.LayoutManager} this
51642          */
51643         "layout" : true,
51644         /**
51645          * @event regionresized
51646          * Fires when the user resizes a region. 
51647          * @param {Roo.LayoutRegion} region The resized region
51648          * @param {Number} newSize The new size (width for east/west, height for north/south)
51649          */
51650         "regionresized" : true,
51651         /**
51652          * @event regioncollapsed
51653          * Fires when a region is collapsed. 
51654          * @param {Roo.LayoutRegion} region The collapsed region
51655          */
51656         "regioncollapsed" : true,
51657         /**
51658          * @event regionexpanded
51659          * Fires when a region is expanded.  
51660          * @param {Roo.LayoutRegion} region The expanded region
51661          */
51662         "regionexpanded" : true
51663     });
51664     this.updating = false;
51665     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
51666 };
51667
51668 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
51669     /**
51670      * Returns true if this layout is currently being updated
51671      * @return {Boolean}
51672      */
51673     isUpdating : function(){
51674         return this.updating; 
51675     },
51676     
51677     /**
51678      * Suspend the LayoutManager from doing auto-layouts while
51679      * making multiple add or remove calls
51680      */
51681     beginUpdate : function(){
51682         this.updating = true;    
51683     },
51684     
51685     /**
51686      * Restore auto-layouts and optionally disable the manager from performing a layout
51687      * @param {Boolean} noLayout true to disable a layout update 
51688      */
51689     endUpdate : function(noLayout){
51690         this.updating = false;
51691         if(!noLayout){
51692             this.layout();
51693         }    
51694     },
51695     
51696     layout: function(){
51697         
51698     },
51699     
51700     onRegionResized : function(region, newSize){
51701         this.fireEvent("regionresized", region, newSize);
51702         this.layout();
51703     },
51704     
51705     onRegionCollapsed : function(region){
51706         this.fireEvent("regioncollapsed", region);
51707     },
51708     
51709     onRegionExpanded : function(region){
51710         this.fireEvent("regionexpanded", region);
51711     },
51712         
51713     /**
51714      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
51715      * performs box-model adjustments.
51716      * @return {Object} The size as an object {width: (the width), height: (the height)}
51717      */
51718     getViewSize : function(){
51719         var size;
51720         if(this.el.dom != document.body){
51721             size = this.el.getSize();
51722         }else{
51723             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
51724         }
51725         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
51726         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
51727         return size;
51728     },
51729     
51730     /**
51731      * Returns the Element this layout is bound to.
51732      * @return {Roo.Element}
51733      */
51734     getEl : function(){
51735         return this.el;
51736     },
51737     
51738     /**
51739      * Returns the specified region.
51740      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
51741      * @return {Roo.LayoutRegion}
51742      */
51743     getRegion : function(target){
51744         return this.regions[target.toLowerCase()];
51745     },
51746     
51747     onWindowResize : function(){
51748         if(this.monitorWindowResize){
51749             this.layout();
51750         }
51751     }
51752 });/*
51753  * Based on:
51754  * Ext JS Library 1.1.1
51755  * Copyright(c) 2006-2007, Ext JS, LLC.
51756  *
51757  * Originally Released Under LGPL - original licence link has changed is not relivant.
51758  *
51759  * Fork - LGPL
51760  * <script type="text/javascript">
51761  */
51762 /**
51763  * @class Roo.BorderLayout
51764  * @extends Roo.LayoutManager
51765  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
51766  * please see: <br><br>
51767  * <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>
51768  * <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>
51769  * Example:
51770  <pre><code>
51771  var layout = new Roo.BorderLayout(document.body, {
51772     north: {
51773         initialSize: 25,
51774         titlebar: false
51775     },
51776     west: {
51777         split:true,
51778         initialSize: 200,
51779         minSize: 175,
51780         maxSize: 400,
51781         titlebar: true,
51782         collapsible: true
51783     },
51784     east: {
51785         split:true,
51786         initialSize: 202,
51787         minSize: 175,
51788         maxSize: 400,
51789         titlebar: true,
51790         collapsible: true
51791     },
51792     south: {
51793         split:true,
51794         initialSize: 100,
51795         minSize: 100,
51796         maxSize: 200,
51797         titlebar: true,
51798         collapsible: true
51799     },
51800     center: {
51801         titlebar: true,
51802         autoScroll:true,
51803         resizeTabs: true,
51804         minTabWidth: 50,
51805         preferredTabWidth: 150
51806     }
51807 });
51808
51809 // shorthand
51810 var CP = Roo.ContentPanel;
51811
51812 layout.beginUpdate();
51813 layout.add("north", new CP("north", "North"));
51814 layout.add("south", new CP("south", {title: "South", closable: true}));
51815 layout.add("west", new CP("west", {title: "West"}));
51816 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
51817 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
51818 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
51819 layout.getRegion("center").showPanel("center1");
51820 layout.endUpdate();
51821 </code></pre>
51822
51823 <b>The container the layout is rendered into can be either the body element or any other element.
51824 If it is not the body element, the container needs to either be an absolute positioned element,
51825 or you will need to add "position:relative" to the css of the container.  You will also need to specify
51826 the container size if it is not the body element.</b>
51827
51828 * @constructor
51829 * Create a new BorderLayout
51830 * @param {String/HTMLElement/Element} container The container this layout is bound to
51831 * @param {Object} config Configuration options
51832  */
51833 Roo.BorderLayout = function(container, config){
51834     config = config || {};
51835     Roo.BorderLayout.superclass.constructor.call(this, container, config);
51836     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
51837     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
51838         var target = this.factory.validRegions[i];
51839         if(config[target]){
51840             this.addRegion(target, config[target]);
51841         }
51842     }
51843 };
51844
51845 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
51846     /**
51847      * Creates and adds a new region if it doesn't already exist.
51848      * @param {String} target The target region key (north, south, east, west or center).
51849      * @param {Object} config The regions config object
51850      * @return {BorderLayoutRegion} The new region
51851      */
51852     addRegion : function(target, config){
51853         if(!this.regions[target]){
51854             var r = this.factory.create(target, this, config);
51855             this.bindRegion(target, r);
51856         }
51857         return this.regions[target];
51858     },
51859
51860     // private (kinda)
51861     bindRegion : function(name, r){
51862         this.regions[name] = r;
51863         r.on("visibilitychange", this.layout, this);
51864         r.on("paneladded", this.layout, this);
51865         r.on("panelremoved", this.layout, this);
51866         r.on("invalidated", this.layout, this);
51867         r.on("resized", this.onRegionResized, this);
51868         r.on("collapsed", this.onRegionCollapsed, this);
51869         r.on("expanded", this.onRegionExpanded, this);
51870     },
51871
51872     /**
51873      * Performs a layout update.
51874      */
51875     layout : function(){
51876         if(this.updating) {
51877             return;
51878         }
51879         var size = this.getViewSize();
51880         var w = size.width;
51881         var h = size.height;
51882         var centerW = w;
51883         var centerH = h;
51884         var centerY = 0;
51885         var centerX = 0;
51886         //var x = 0, y = 0;
51887
51888         var rs = this.regions;
51889         var north = rs["north"];
51890         var south = rs["south"]; 
51891         var west = rs["west"];
51892         var east = rs["east"];
51893         var center = rs["center"];
51894         //if(this.hideOnLayout){ // not supported anymore
51895             //c.el.setStyle("display", "none");
51896         //}
51897         if(north && north.isVisible()){
51898             var b = north.getBox();
51899             var m = north.getMargins();
51900             b.width = w - (m.left+m.right);
51901             b.x = m.left;
51902             b.y = m.top;
51903             centerY = b.height + b.y + m.bottom;
51904             centerH -= centerY;
51905             north.updateBox(this.safeBox(b));
51906         }
51907         if(south && south.isVisible()){
51908             var b = south.getBox();
51909             var m = south.getMargins();
51910             b.width = w - (m.left+m.right);
51911             b.x = m.left;
51912             var totalHeight = (b.height + m.top + m.bottom);
51913             b.y = h - totalHeight + m.top;
51914             centerH -= totalHeight;
51915             south.updateBox(this.safeBox(b));
51916         }
51917         if(west && west.isVisible()){
51918             var b = west.getBox();
51919             var m = west.getMargins();
51920             b.height = centerH - (m.top+m.bottom);
51921             b.x = m.left;
51922             b.y = centerY + m.top;
51923             var totalWidth = (b.width + m.left + m.right);
51924             centerX += totalWidth;
51925             centerW -= totalWidth;
51926             west.updateBox(this.safeBox(b));
51927         }
51928         if(east && east.isVisible()){
51929             var b = east.getBox();
51930             var m = east.getMargins();
51931             b.height = centerH - (m.top+m.bottom);
51932             var totalWidth = (b.width + m.left + m.right);
51933             b.x = w - totalWidth + m.left;
51934             b.y = centerY + m.top;
51935             centerW -= totalWidth;
51936             east.updateBox(this.safeBox(b));
51937         }
51938         if(center){
51939             var m = center.getMargins();
51940             var centerBox = {
51941                 x: centerX + m.left,
51942                 y: centerY + m.top,
51943                 width: centerW - (m.left+m.right),
51944                 height: centerH - (m.top+m.bottom)
51945             };
51946             //if(this.hideOnLayout){
51947                 //center.el.setStyle("display", "block");
51948             //}
51949             center.updateBox(this.safeBox(centerBox));
51950         }
51951         this.el.repaint();
51952         this.fireEvent("layout", this);
51953     },
51954
51955     // private
51956     safeBox : function(box){
51957         box.width = Math.max(0, box.width);
51958         box.height = Math.max(0, box.height);
51959         return box;
51960     },
51961
51962     /**
51963      * Adds a ContentPanel (or subclass) to this layout.
51964      * @param {String} target The target region key (north, south, east, west or center).
51965      * @param {Roo.ContentPanel} panel The panel to add
51966      * @return {Roo.ContentPanel} The added panel
51967      */
51968     add : function(target, panel){
51969          
51970         target = target.toLowerCase();
51971         return this.regions[target].add(panel);
51972     },
51973
51974     /**
51975      * Remove a ContentPanel (or subclass) to this layout.
51976      * @param {String} target The target region key (north, south, east, west or center).
51977      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51978      * @return {Roo.ContentPanel} The removed panel
51979      */
51980     remove : function(target, panel){
51981         target = target.toLowerCase();
51982         return this.regions[target].remove(panel);
51983     },
51984
51985     /**
51986      * Searches all regions for a panel with the specified id
51987      * @param {String} panelId
51988      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51989      */
51990     findPanel : function(panelId){
51991         var rs = this.regions;
51992         for(var target in rs){
51993             if(typeof rs[target] != "function"){
51994                 var p = rs[target].getPanel(panelId);
51995                 if(p){
51996                     return p;
51997                 }
51998             }
51999         }
52000         return null;
52001     },
52002
52003     /**
52004      * Searches all regions for a panel with the specified id and activates (shows) it.
52005      * @param {String/ContentPanel} panelId The panels id or the panel itself
52006      * @return {Roo.ContentPanel} The shown panel or null
52007      */
52008     showPanel : function(panelId) {
52009       var rs = this.regions;
52010       for(var target in rs){
52011          var r = rs[target];
52012          if(typeof r != "function"){
52013             if(r.hasPanel(panelId)){
52014                return r.showPanel(panelId);
52015             }
52016          }
52017       }
52018       return null;
52019    },
52020
52021    /**
52022      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52023      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52024      */
52025     restoreState : function(provider){
52026         if(!provider){
52027             provider = Roo.state.Manager;
52028         }
52029         var sm = new Roo.LayoutStateManager();
52030         sm.init(this, provider);
52031     },
52032
52033     /**
52034      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52035      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52036      * a valid ContentPanel config object.  Example:
52037      * <pre><code>
52038 // Create the main layout
52039 var layout = new Roo.BorderLayout('main-ct', {
52040     west: {
52041         split:true,
52042         minSize: 175,
52043         titlebar: true
52044     },
52045     center: {
52046         title:'Components'
52047     }
52048 }, 'main-ct');
52049
52050 // Create and add multiple ContentPanels at once via configs
52051 layout.batchAdd({
52052    west: {
52053        id: 'source-files',
52054        autoCreate:true,
52055        title:'Ext Source Files',
52056        autoScroll:true,
52057        fitToFrame:true
52058    },
52059    center : {
52060        el: cview,
52061        autoScroll:true,
52062        fitToFrame:true,
52063        toolbar: tb,
52064        resizeEl:'cbody'
52065    }
52066 });
52067 </code></pre>
52068      * @param {Object} regions An object containing ContentPanel configs by region name
52069      */
52070     batchAdd : function(regions){
52071         this.beginUpdate();
52072         for(var rname in regions){
52073             var lr = this.regions[rname];
52074             if(lr){
52075                 this.addTypedPanels(lr, regions[rname]);
52076             }
52077         }
52078         this.endUpdate();
52079     },
52080
52081     // private
52082     addTypedPanels : function(lr, ps){
52083         if(typeof ps == 'string'){
52084             lr.add(new Roo.ContentPanel(ps));
52085         }
52086         else if(ps instanceof Array){
52087             for(var i =0, len = ps.length; i < len; i++){
52088                 this.addTypedPanels(lr, ps[i]);
52089             }
52090         }
52091         else if(!ps.events){ // raw config?
52092             var el = ps.el;
52093             delete ps.el; // prevent conflict
52094             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52095         }
52096         else {  // panel object assumed!
52097             lr.add(ps);
52098         }
52099     },
52100     /**
52101      * Adds a xtype elements to the layout.
52102      * <pre><code>
52103
52104 layout.addxtype({
52105        xtype : 'ContentPanel',
52106        region: 'west',
52107        items: [ .... ]
52108    }
52109 );
52110
52111 layout.addxtype({
52112         xtype : 'NestedLayoutPanel',
52113         region: 'west',
52114         layout: {
52115            center: { },
52116            west: { }   
52117         },
52118         items : [ ... list of content panels or nested layout panels.. ]
52119    }
52120 );
52121 </code></pre>
52122      * @param {Object} cfg Xtype definition of item to add.
52123      */
52124     addxtype : function(cfg)
52125     {
52126         // basically accepts a pannel...
52127         // can accept a layout region..!?!?
52128         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52129         
52130         if (!cfg.xtype.match(/Panel$/)) {
52131             return false;
52132         }
52133         var ret = false;
52134         
52135         if (typeof(cfg.region) == 'undefined') {
52136             Roo.log("Failed to add Panel, region was not set");
52137             Roo.log(cfg);
52138             return false;
52139         }
52140         var region = cfg.region;
52141         delete cfg.region;
52142         
52143           
52144         var xitems = [];
52145         if (cfg.items) {
52146             xitems = cfg.items;
52147             delete cfg.items;
52148         }
52149         var nb = false;
52150         
52151         switch(cfg.xtype) 
52152         {
52153             case 'ContentPanel':  // ContentPanel (el, cfg)
52154             case 'ScrollPanel':  // ContentPanel (el, cfg)
52155             case 'ViewPanel': 
52156                 if(cfg.autoCreate) {
52157                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52158                 } else {
52159                     var el = this.el.createChild();
52160                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52161                 }
52162                 
52163                 this.add(region, ret);
52164                 break;
52165             
52166             
52167             case 'TreePanel': // our new panel!
52168                 cfg.el = this.el.createChild();
52169                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52170                 this.add(region, ret);
52171                 break;
52172             
52173             case 'NestedLayoutPanel': 
52174                 // create a new Layout (which is  a Border Layout...
52175                 var el = this.el.createChild();
52176                 var clayout = cfg.layout;
52177                 delete cfg.layout;
52178                 clayout.items   = clayout.items  || [];
52179                 // replace this exitems with the clayout ones..
52180                 xitems = clayout.items;
52181                  
52182                 
52183                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52184                     cfg.background = false;
52185                 }
52186                 var layout = new Roo.BorderLayout(el, clayout);
52187                 
52188                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52189                 //console.log('adding nested layout panel '  + cfg.toSource());
52190                 this.add(region, ret);
52191                 nb = {}; /// find first...
52192                 break;
52193                 
52194             case 'GridPanel': 
52195             
52196                 // needs grid and region
52197                 
52198                 //var el = this.getRegion(region).el.createChild();
52199                 var el = this.el.createChild();
52200                 // create the grid first...
52201                 
52202                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52203                 delete cfg.grid;
52204                 if (region == 'center' && this.active ) {
52205                     cfg.background = false;
52206                 }
52207                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52208                 
52209                 this.add(region, ret);
52210                 if (cfg.background) {
52211                     ret.on('activate', function(gp) {
52212                         if (!gp.grid.rendered) {
52213                             gp.grid.render();
52214                         }
52215                     });
52216                 } else {
52217                     grid.render();
52218                 }
52219                 break;
52220            
52221            
52222            
52223                 
52224                 
52225                 
52226             default:
52227                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52228                     
52229                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52230                     this.add(region, ret);
52231                 } else {
52232                 
52233                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52234                     return null;
52235                 }
52236                 
52237              // GridPanel (grid, cfg)
52238             
52239         }
52240         this.beginUpdate();
52241         // add children..
52242         var region = '';
52243         var abn = {};
52244         Roo.each(xitems, function(i)  {
52245             region = nb && i.region ? i.region : false;
52246             
52247             var add = ret.addxtype(i);
52248            
52249             if (region) {
52250                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52251                 if (!i.background) {
52252                     abn[region] = nb[region] ;
52253                 }
52254             }
52255             
52256         });
52257         this.endUpdate();
52258
52259         // make the last non-background panel active..
52260         //if (nb) { Roo.log(abn); }
52261         if (nb) {
52262             
52263             for(var r in abn) {
52264                 region = this.getRegion(r);
52265                 if (region) {
52266                     // tried using nb[r], but it does not work..
52267                      
52268                     region.showPanel(abn[r]);
52269                    
52270                 }
52271             }
52272         }
52273         return ret;
52274         
52275     }
52276 });
52277
52278 /**
52279  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52280  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52281  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52282  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52283  * <pre><code>
52284 // shorthand
52285 var CP = Roo.ContentPanel;
52286
52287 var layout = Roo.BorderLayout.create({
52288     north: {
52289         initialSize: 25,
52290         titlebar: false,
52291         panels: [new CP("north", "North")]
52292     },
52293     west: {
52294         split:true,
52295         initialSize: 200,
52296         minSize: 175,
52297         maxSize: 400,
52298         titlebar: true,
52299         collapsible: true,
52300         panels: [new CP("west", {title: "West"})]
52301     },
52302     east: {
52303         split:true,
52304         initialSize: 202,
52305         minSize: 175,
52306         maxSize: 400,
52307         titlebar: true,
52308         collapsible: true,
52309         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52310     },
52311     south: {
52312         split:true,
52313         initialSize: 100,
52314         minSize: 100,
52315         maxSize: 200,
52316         titlebar: true,
52317         collapsible: true,
52318         panels: [new CP("south", {title: "South", closable: true})]
52319     },
52320     center: {
52321         titlebar: true,
52322         autoScroll:true,
52323         resizeTabs: true,
52324         minTabWidth: 50,
52325         preferredTabWidth: 150,
52326         panels: [
52327             new CP("center1", {title: "Close Me", closable: true}),
52328             new CP("center2", {title: "Center Panel", closable: false})
52329         ]
52330     }
52331 }, document.body);
52332
52333 layout.getRegion("center").showPanel("center1");
52334 </code></pre>
52335  * @param config
52336  * @param targetEl
52337  */
52338 Roo.BorderLayout.create = function(config, targetEl){
52339     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52340     layout.beginUpdate();
52341     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52342     for(var j = 0, jlen = regions.length; j < jlen; j++){
52343         var lr = regions[j];
52344         if(layout.regions[lr] && config[lr].panels){
52345             var r = layout.regions[lr];
52346             var ps = config[lr].panels;
52347             layout.addTypedPanels(r, ps);
52348         }
52349     }
52350     layout.endUpdate();
52351     return layout;
52352 };
52353
52354 // private
52355 Roo.BorderLayout.RegionFactory = {
52356     // private
52357     validRegions : ["north","south","east","west","center"],
52358
52359     // private
52360     create : function(target, mgr, config){
52361         target = target.toLowerCase();
52362         if(config.lightweight || config.basic){
52363             return new Roo.BasicLayoutRegion(mgr, config, target);
52364         }
52365         switch(target){
52366             case "north":
52367                 return new Roo.NorthLayoutRegion(mgr, config);
52368             case "south":
52369                 return new Roo.SouthLayoutRegion(mgr, config);
52370             case "east":
52371                 return new Roo.EastLayoutRegion(mgr, config);
52372             case "west":
52373                 return new Roo.WestLayoutRegion(mgr, config);
52374             case "center":
52375                 return new Roo.CenterLayoutRegion(mgr, config);
52376         }
52377         throw 'Layout region "'+target+'" not supported.';
52378     }
52379 };/*
52380  * Based on:
52381  * Ext JS Library 1.1.1
52382  * Copyright(c) 2006-2007, Ext JS, LLC.
52383  *
52384  * Originally Released Under LGPL - original licence link has changed is not relivant.
52385  *
52386  * Fork - LGPL
52387  * <script type="text/javascript">
52388  */
52389  
52390 /**
52391  * @class Roo.BasicLayoutRegion
52392  * @extends Roo.util.Observable
52393  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52394  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52395  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52396  */
52397 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52398     this.mgr = mgr;
52399     this.position  = pos;
52400     this.events = {
52401         /**
52402          * @scope Roo.BasicLayoutRegion
52403          */
52404         
52405         /**
52406          * @event beforeremove
52407          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52408          * @param {Roo.LayoutRegion} this
52409          * @param {Roo.ContentPanel} panel The panel
52410          * @param {Object} e The cancel event object
52411          */
52412         "beforeremove" : true,
52413         /**
52414          * @event invalidated
52415          * Fires when the layout for this region is changed.
52416          * @param {Roo.LayoutRegion} this
52417          */
52418         "invalidated" : true,
52419         /**
52420          * @event visibilitychange
52421          * Fires when this region is shown or hidden 
52422          * @param {Roo.LayoutRegion} this
52423          * @param {Boolean} visibility true or false
52424          */
52425         "visibilitychange" : true,
52426         /**
52427          * @event paneladded
52428          * Fires when a panel is added. 
52429          * @param {Roo.LayoutRegion} this
52430          * @param {Roo.ContentPanel} panel The panel
52431          */
52432         "paneladded" : true,
52433         /**
52434          * @event panelremoved
52435          * Fires when a panel is removed. 
52436          * @param {Roo.LayoutRegion} this
52437          * @param {Roo.ContentPanel} panel The panel
52438          */
52439         "panelremoved" : true,
52440         /**
52441          * @event beforecollapse
52442          * Fires when this region before collapse.
52443          * @param {Roo.LayoutRegion} this
52444          */
52445         "beforecollapse" : true,
52446         /**
52447          * @event collapsed
52448          * Fires when this region is collapsed.
52449          * @param {Roo.LayoutRegion} this
52450          */
52451         "collapsed" : true,
52452         /**
52453          * @event expanded
52454          * Fires when this region is expanded.
52455          * @param {Roo.LayoutRegion} this
52456          */
52457         "expanded" : true,
52458         /**
52459          * @event slideshow
52460          * Fires when this region is slid into view.
52461          * @param {Roo.LayoutRegion} this
52462          */
52463         "slideshow" : true,
52464         /**
52465          * @event slidehide
52466          * Fires when this region slides out of view. 
52467          * @param {Roo.LayoutRegion} this
52468          */
52469         "slidehide" : true,
52470         /**
52471          * @event panelactivated
52472          * Fires when a panel is activated. 
52473          * @param {Roo.LayoutRegion} this
52474          * @param {Roo.ContentPanel} panel The activated panel
52475          */
52476         "panelactivated" : true,
52477         /**
52478          * @event resized
52479          * Fires when the user resizes this region. 
52480          * @param {Roo.LayoutRegion} this
52481          * @param {Number} newSize The new size (width for east/west, height for north/south)
52482          */
52483         "resized" : true
52484     };
52485     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52486     this.panels = new Roo.util.MixedCollection();
52487     this.panels.getKey = this.getPanelId.createDelegate(this);
52488     this.box = null;
52489     this.activePanel = null;
52490     // ensure listeners are added...
52491     
52492     if (config.listeners || config.events) {
52493         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52494             listeners : config.listeners || {},
52495             events : config.events || {}
52496         });
52497     }
52498     
52499     if(skipConfig !== true){
52500         this.applyConfig(config);
52501     }
52502 };
52503
52504 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52505     getPanelId : function(p){
52506         return p.getId();
52507     },
52508     
52509     applyConfig : function(config){
52510         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52511         this.config = config;
52512         
52513     },
52514     
52515     /**
52516      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52517      * the width, for horizontal (north, south) the height.
52518      * @param {Number} newSize The new width or height
52519      */
52520     resizeTo : function(newSize){
52521         var el = this.el ? this.el :
52522                  (this.activePanel ? this.activePanel.getEl() : null);
52523         if(el){
52524             switch(this.position){
52525                 case "east":
52526                 case "west":
52527                     el.setWidth(newSize);
52528                     this.fireEvent("resized", this, newSize);
52529                 break;
52530                 case "north":
52531                 case "south":
52532                     el.setHeight(newSize);
52533                     this.fireEvent("resized", this, newSize);
52534                 break;                
52535             }
52536         }
52537     },
52538     
52539     getBox : function(){
52540         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52541     },
52542     
52543     getMargins : function(){
52544         return this.margins;
52545     },
52546     
52547     updateBox : function(box){
52548         this.box = box;
52549         var el = this.activePanel.getEl();
52550         el.dom.style.left = box.x + "px";
52551         el.dom.style.top = box.y + "px";
52552         this.activePanel.setSize(box.width, box.height);
52553     },
52554     
52555     /**
52556      * Returns the container element for this region.
52557      * @return {Roo.Element}
52558      */
52559     getEl : function(){
52560         return this.activePanel;
52561     },
52562     
52563     /**
52564      * Returns true if this region is currently visible.
52565      * @return {Boolean}
52566      */
52567     isVisible : function(){
52568         return this.activePanel ? true : false;
52569     },
52570     
52571     setActivePanel : function(panel){
52572         panel = this.getPanel(panel);
52573         if(this.activePanel && this.activePanel != panel){
52574             this.activePanel.setActiveState(false);
52575             this.activePanel.getEl().setLeftTop(-10000,-10000);
52576         }
52577         this.activePanel = panel;
52578         panel.setActiveState(true);
52579         if(this.box){
52580             panel.setSize(this.box.width, this.box.height);
52581         }
52582         this.fireEvent("panelactivated", this, panel);
52583         this.fireEvent("invalidated");
52584     },
52585     
52586     /**
52587      * Show the specified panel.
52588      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
52589      * @return {Roo.ContentPanel} The shown panel or null
52590      */
52591     showPanel : function(panel){
52592         if(panel = this.getPanel(panel)){
52593             this.setActivePanel(panel);
52594         }
52595         return panel;
52596     },
52597     
52598     /**
52599      * Get the active panel for this region.
52600      * @return {Roo.ContentPanel} The active panel or null
52601      */
52602     getActivePanel : function(){
52603         return this.activePanel;
52604     },
52605     
52606     /**
52607      * Add the passed ContentPanel(s)
52608      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52609      * @return {Roo.ContentPanel} The panel added (if only one was added)
52610      */
52611     add : function(panel){
52612         if(arguments.length > 1){
52613             for(var i = 0, len = arguments.length; i < len; i++) {
52614                 this.add(arguments[i]);
52615             }
52616             return null;
52617         }
52618         if(this.hasPanel(panel)){
52619             this.showPanel(panel);
52620             return panel;
52621         }
52622         var el = panel.getEl();
52623         if(el.dom.parentNode != this.mgr.el.dom){
52624             this.mgr.el.dom.appendChild(el.dom);
52625         }
52626         if(panel.setRegion){
52627             panel.setRegion(this);
52628         }
52629         this.panels.add(panel);
52630         el.setStyle("position", "absolute");
52631         if(!panel.background){
52632             this.setActivePanel(panel);
52633             if(this.config.initialSize && this.panels.getCount()==1){
52634                 this.resizeTo(this.config.initialSize);
52635             }
52636         }
52637         this.fireEvent("paneladded", this, panel);
52638         return panel;
52639     },
52640     
52641     /**
52642      * Returns true if the panel is in this region.
52643      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52644      * @return {Boolean}
52645      */
52646     hasPanel : function(panel){
52647         if(typeof panel == "object"){ // must be panel obj
52648             panel = panel.getId();
52649         }
52650         return this.getPanel(panel) ? true : false;
52651     },
52652     
52653     /**
52654      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52655      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52656      * @param {Boolean} preservePanel Overrides the config preservePanel option
52657      * @return {Roo.ContentPanel} The panel that was removed
52658      */
52659     remove : function(panel, preservePanel){
52660         panel = this.getPanel(panel);
52661         if(!panel){
52662             return null;
52663         }
52664         var e = {};
52665         this.fireEvent("beforeremove", this, panel, e);
52666         if(e.cancel === true){
52667             return null;
52668         }
52669         var panelId = panel.getId();
52670         this.panels.removeKey(panelId);
52671         return panel;
52672     },
52673     
52674     /**
52675      * Returns the panel specified or null if it's not in this region.
52676      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52677      * @return {Roo.ContentPanel}
52678      */
52679     getPanel : function(id){
52680         if(typeof id == "object"){ // must be panel obj
52681             return id;
52682         }
52683         return this.panels.get(id);
52684     },
52685     
52686     /**
52687      * Returns this regions position (north/south/east/west/center).
52688      * @return {String} 
52689      */
52690     getPosition: function(){
52691         return this.position;    
52692     }
52693 });/*
52694  * Based on:
52695  * Ext JS Library 1.1.1
52696  * Copyright(c) 2006-2007, Ext JS, LLC.
52697  *
52698  * Originally Released Under LGPL - original licence link has changed is not relivant.
52699  *
52700  * Fork - LGPL
52701  * <script type="text/javascript">
52702  */
52703  
52704 /**
52705  * @class Roo.LayoutRegion
52706  * @extends Roo.BasicLayoutRegion
52707  * This class represents a region in a layout manager.
52708  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
52709  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
52710  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
52711  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
52712  * @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})
52713  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
52714  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
52715  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
52716  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
52717  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
52718  * @cfg {String}    title           The title for the region (overrides panel titles)
52719  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
52720  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
52721  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
52722  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
52723  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
52724  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
52725  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
52726  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
52727  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
52728  * @cfg {Boolean}   showPin         True to show a pin button
52729  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
52730  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
52731  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
52732  * @cfg {Number}    width           For East/West panels
52733  * @cfg {Number}    height          For North/South panels
52734  * @cfg {Boolean}   split           To show the splitter
52735  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
52736  */
52737 Roo.LayoutRegion = function(mgr, config, pos){
52738     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
52739     var dh = Roo.DomHelper;
52740     /** This region's container element 
52741     * @type Roo.Element */
52742     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
52743     /** This region's title element 
52744     * @type Roo.Element */
52745
52746     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
52747         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
52748         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
52749     ]}, true);
52750     this.titleEl.enableDisplayMode();
52751     /** This region's title text element 
52752     * @type HTMLElement */
52753     this.titleTextEl = this.titleEl.dom.firstChild;
52754     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
52755     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
52756     this.closeBtn.enableDisplayMode();
52757     this.closeBtn.on("click", this.closeClicked, this);
52758     this.closeBtn.hide();
52759
52760     this.createBody(config);
52761     this.visible = true;
52762     this.collapsed = false;
52763
52764     if(config.hideWhenEmpty){
52765         this.hide();
52766         this.on("paneladded", this.validateVisibility, this);
52767         this.on("panelremoved", this.validateVisibility, this);
52768     }
52769     this.applyConfig(config);
52770 };
52771
52772 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
52773
52774     createBody : function(){
52775         /** This region's body element 
52776         * @type Roo.Element */
52777         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
52778     },
52779
52780     applyConfig : function(c){
52781         if(c.collapsible && this.position != "center" && !this.collapsedEl){
52782             var dh = Roo.DomHelper;
52783             if(c.titlebar !== false){
52784                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
52785                 this.collapseBtn.on("click", this.collapse, this);
52786                 this.collapseBtn.enableDisplayMode();
52787
52788                 if(c.showPin === true || this.showPin){
52789                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
52790                     this.stickBtn.enableDisplayMode();
52791                     this.stickBtn.on("click", this.expand, this);
52792                     this.stickBtn.hide();
52793                 }
52794             }
52795             /** This region's collapsed element
52796             * @type Roo.Element */
52797             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
52798                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
52799             ]}, true);
52800             if(c.floatable !== false){
52801                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
52802                this.collapsedEl.on("click", this.collapseClick, this);
52803             }
52804
52805             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
52806                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
52807                    id: "message", unselectable: "on", style:{"float":"left"}});
52808                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
52809              }
52810             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
52811             this.expandBtn.on("click", this.expand, this);
52812         }
52813         if(this.collapseBtn){
52814             this.collapseBtn.setVisible(c.collapsible == true);
52815         }
52816         this.cmargins = c.cmargins || this.cmargins ||
52817                          (this.position == "west" || this.position == "east" ?
52818                              {top: 0, left: 2, right:2, bottom: 0} :
52819                              {top: 2, left: 0, right:0, bottom: 2});
52820         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52821         this.bottomTabs = c.tabPosition != "top";
52822         this.autoScroll = c.autoScroll || false;
52823         if(this.autoScroll){
52824             this.bodyEl.setStyle("overflow", "auto");
52825         }else{
52826             this.bodyEl.setStyle("overflow", "hidden");
52827         }
52828         //if(c.titlebar !== false){
52829             if((!c.titlebar && !c.title) || c.titlebar === false){
52830                 this.titleEl.hide();
52831             }else{
52832                 this.titleEl.show();
52833                 if(c.title){
52834                     this.titleTextEl.innerHTML = c.title;
52835                 }
52836             }
52837         //}
52838         this.duration = c.duration || .30;
52839         this.slideDuration = c.slideDuration || .45;
52840         this.config = c;
52841         if(c.collapsed){
52842             this.collapse(true);
52843         }
52844         if(c.hidden){
52845             this.hide();
52846         }
52847     },
52848     /**
52849      * Returns true if this region is currently visible.
52850      * @return {Boolean}
52851      */
52852     isVisible : function(){
52853         return this.visible;
52854     },
52855
52856     /**
52857      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
52858      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
52859      */
52860     setCollapsedTitle : function(title){
52861         title = title || "&#160;";
52862         if(this.collapsedTitleTextEl){
52863             this.collapsedTitleTextEl.innerHTML = title;
52864         }
52865     },
52866
52867     getBox : function(){
52868         var b;
52869         if(!this.collapsed){
52870             b = this.el.getBox(false, true);
52871         }else{
52872             b = this.collapsedEl.getBox(false, true);
52873         }
52874         return b;
52875     },
52876
52877     getMargins : function(){
52878         return this.collapsed ? this.cmargins : this.margins;
52879     },
52880
52881     highlight : function(){
52882         this.el.addClass("x-layout-panel-dragover");
52883     },
52884
52885     unhighlight : function(){
52886         this.el.removeClass("x-layout-panel-dragover");
52887     },
52888
52889     updateBox : function(box){
52890         this.box = box;
52891         if(!this.collapsed){
52892             this.el.dom.style.left = box.x + "px";
52893             this.el.dom.style.top = box.y + "px";
52894             this.updateBody(box.width, box.height);
52895         }else{
52896             this.collapsedEl.dom.style.left = box.x + "px";
52897             this.collapsedEl.dom.style.top = box.y + "px";
52898             this.collapsedEl.setSize(box.width, box.height);
52899         }
52900         if(this.tabs){
52901             this.tabs.autoSizeTabs();
52902         }
52903     },
52904
52905     updateBody : function(w, h){
52906         if(w !== null){
52907             this.el.setWidth(w);
52908             w -= this.el.getBorderWidth("rl");
52909             if(this.config.adjustments){
52910                 w += this.config.adjustments[0];
52911             }
52912         }
52913         if(h !== null){
52914             this.el.setHeight(h);
52915             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52916             h -= this.el.getBorderWidth("tb");
52917             if(this.config.adjustments){
52918                 h += this.config.adjustments[1];
52919             }
52920             this.bodyEl.setHeight(h);
52921             if(this.tabs){
52922                 h = this.tabs.syncHeight(h);
52923             }
52924         }
52925         if(this.panelSize){
52926             w = w !== null ? w : this.panelSize.width;
52927             h = h !== null ? h : this.panelSize.height;
52928         }
52929         if(this.activePanel){
52930             var el = this.activePanel.getEl();
52931             w = w !== null ? w : el.getWidth();
52932             h = h !== null ? h : el.getHeight();
52933             this.panelSize = {width: w, height: h};
52934             this.activePanel.setSize(w, h);
52935         }
52936         if(Roo.isIE && this.tabs){
52937             this.tabs.el.repaint();
52938         }
52939     },
52940
52941     /**
52942      * Returns the container element for this region.
52943      * @return {Roo.Element}
52944      */
52945     getEl : function(){
52946         return this.el;
52947     },
52948
52949     /**
52950      * Hides this region.
52951      */
52952     hide : function(){
52953         if(!this.collapsed){
52954             this.el.dom.style.left = "-2000px";
52955             this.el.hide();
52956         }else{
52957             this.collapsedEl.dom.style.left = "-2000px";
52958             this.collapsedEl.hide();
52959         }
52960         this.visible = false;
52961         this.fireEvent("visibilitychange", this, false);
52962     },
52963
52964     /**
52965      * Shows this region if it was previously hidden.
52966      */
52967     show : function(){
52968         if(!this.collapsed){
52969             this.el.show();
52970         }else{
52971             this.collapsedEl.show();
52972         }
52973         this.visible = true;
52974         this.fireEvent("visibilitychange", this, true);
52975     },
52976
52977     closeClicked : function(){
52978         if(this.activePanel){
52979             this.remove(this.activePanel);
52980         }
52981     },
52982
52983     collapseClick : function(e){
52984         if(this.isSlid){
52985            e.stopPropagation();
52986            this.slideIn();
52987         }else{
52988            e.stopPropagation();
52989            this.slideOut();
52990         }
52991     },
52992
52993     /**
52994      * Collapses this region.
52995      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52996      */
52997     collapse : function(skipAnim, skipCheck){
52998         if(this.collapsed) {
52999             return;
53000         }
53001         
53002         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53003             
53004             this.collapsed = true;
53005             if(this.split){
53006                 this.split.el.hide();
53007             }
53008             if(this.config.animate && skipAnim !== true){
53009                 this.fireEvent("invalidated", this);
53010                 this.animateCollapse();
53011             }else{
53012                 this.el.setLocation(-20000,-20000);
53013                 this.el.hide();
53014                 this.collapsedEl.show();
53015                 this.fireEvent("collapsed", this);
53016                 this.fireEvent("invalidated", this);
53017             }
53018         }
53019         
53020     },
53021
53022     animateCollapse : function(){
53023         // overridden
53024     },
53025
53026     /**
53027      * Expands this region if it was previously collapsed.
53028      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53029      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53030      */
53031     expand : function(e, skipAnim){
53032         if(e) {
53033             e.stopPropagation();
53034         }
53035         if(!this.collapsed || this.el.hasActiveFx()) {
53036             return;
53037         }
53038         if(this.isSlid){
53039             this.afterSlideIn();
53040             skipAnim = true;
53041         }
53042         this.collapsed = false;
53043         if(this.config.animate && skipAnim !== true){
53044             this.animateExpand();
53045         }else{
53046             this.el.show();
53047             if(this.split){
53048                 this.split.el.show();
53049             }
53050             this.collapsedEl.setLocation(-2000,-2000);
53051             this.collapsedEl.hide();
53052             this.fireEvent("invalidated", this);
53053             this.fireEvent("expanded", this);
53054         }
53055     },
53056
53057     animateExpand : function(){
53058         // overridden
53059     },
53060
53061     initTabs : function()
53062     {
53063         this.bodyEl.setStyle("overflow", "hidden");
53064         var ts = new Roo.TabPanel(
53065                 this.bodyEl.dom,
53066                 {
53067                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53068                     disableTooltips: this.config.disableTabTips,
53069                     toolbar : this.config.toolbar
53070                 }
53071         );
53072         if(this.config.hideTabs){
53073             ts.stripWrap.setDisplayed(false);
53074         }
53075         this.tabs = ts;
53076         ts.resizeTabs = this.config.resizeTabs === true;
53077         ts.minTabWidth = this.config.minTabWidth || 40;
53078         ts.maxTabWidth = this.config.maxTabWidth || 250;
53079         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53080         ts.monitorResize = false;
53081         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53082         ts.bodyEl.addClass('x-layout-tabs-body');
53083         this.panels.each(this.initPanelAsTab, this);
53084     },
53085
53086     initPanelAsTab : function(panel){
53087         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53088                     this.config.closeOnTab && panel.isClosable());
53089         if(panel.tabTip !== undefined){
53090             ti.setTooltip(panel.tabTip);
53091         }
53092         ti.on("activate", function(){
53093               this.setActivePanel(panel);
53094         }, this);
53095         if(this.config.closeOnTab){
53096             ti.on("beforeclose", function(t, e){
53097                 e.cancel = true;
53098                 this.remove(panel);
53099             }, this);
53100         }
53101         return ti;
53102     },
53103
53104     updatePanelTitle : function(panel, title){
53105         if(this.activePanel == panel){
53106             this.updateTitle(title);
53107         }
53108         if(this.tabs){
53109             var ti = this.tabs.getTab(panel.getEl().id);
53110             ti.setText(title);
53111             if(panel.tabTip !== undefined){
53112                 ti.setTooltip(panel.tabTip);
53113             }
53114         }
53115     },
53116
53117     updateTitle : function(title){
53118         if(this.titleTextEl && !this.config.title){
53119             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53120         }
53121     },
53122
53123     setActivePanel : function(panel){
53124         panel = this.getPanel(panel);
53125         if(this.activePanel && this.activePanel != panel){
53126             this.activePanel.setActiveState(false);
53127         }
53128         this.activePanel = panel;
53129         panel.setActiveState(true);
53130         if(this.panelSize){
53131             panel.setSize(this.panelSize.width, this.panelSize.height);
53132         }
53133         if(this.closeBtn){
53134             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53135         }
53136         this.updateTitle(panel.getTitle());
53137         if(this.tabs){
53138             this.fireEvent("invalidated", this);
53139         }
53140         this.fireEvent("panelactivated", this, panel);
53141     },
53142
53143     /**
53144      * Shows the specified panel.
53145      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53146      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53147      */
53148     showPanel : function(panel)
53149     {
53150         panel = this.getPanel(panel);
53151         if(panel){
53152             if(this.tabs){
53153                 var tab = this.tabs.getTab(panel.getEl().id);
53154                 if(tab.isHidden()){
53155                     this.tabs.unhideTab(tab.id);
53156                 }
53157                 tab.activate();
53158             }else{
53159                 this.setActivePanel(panel);
53160             }
53161         }
53162         return panel;
53163     },
53164
53165     /**
53166      * Get the active panel for this region.
53167      * @return {Roo.ContentPanel} The active panel or null
53168      */
53169     getActivePanel : function(){
53170         return this.activePanel;
53171     },
53172
53173     validateVisibility : function(){
53174         if(this.panels.getCount() < 1){
53175             this.updateTitle("&#160;");
53176             this.closeBtn.hide();
53177             this.hide();
53178         }else{
53179             if(!this.isVisible()){
53180                 this.show();
53181             }
53182         }
53183     },
53184
53185     /**
53186      * Adds the passed ContentPanel(s) to this region.
53187      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53188      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53189      */
53190     add : function(panel){
53191         if(arguments.length > 1){
53192             for(var i = 0, len = arguments.length; i < len; i++) {
53193                 this.add(arguments[i]);
53194             }
53195             return null;
53196         }
53197         if(this.hasPanel(panel)){
53198             this.showPanel(panel);
53199             return panel;
53200         }
53201         panel.setRegion(this);
53202         this.panels.add(panel);
53203         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53204             this.bodyEl.dom.appendChild(panel.getEl().dom);
53205             if(panel.background !== true){
53206                 this.setActivePanel(panel);
53207             }
53208             this.fireEvent("paneladded", this, panel);
53209             return panel;
53210         }
53211         if(!this.tabs){
53212             this.initTabs();
53213         }else{
53214             this.initPanelAsTab(panel);
53215         }
53216         if(panel.background !== true){
53217             this.tabs.activate(panel.getEl().id);
53218         }
53219         this.fireEvent("paneladded", this, panel);
53220         return panel;
53221     },
53222
53223     /**
53224      * Hides the tab for the specified panel.
53225      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53226      */
53227     hidePanel : function(panel){
53228         if(this.tabs && (panel = this.getPanel(panel))){
53229             this.tabs.hideTab(panel.getEl().id);
53230         }
53231     },
53232
53233     /**
53234      * Unhides the tab for a previously hidden panel.
53235      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53236      */
53237     unhidePanel : function(panel){
53238         if(this.tabs && (panel = this.getPanel(panel))){
53239             this.tabs.unhideTab(panel.getEl().id);
53240         }
53241     },
53242
53243     clearPanels : function(){
53244         while(this.panels.getCount() > 0){
53245              this.remove(this.panels.first());
53246         }
53247     },
53248
53249     /**
53250      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53251      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53252      * @param {Boolean} preservePanel Overrides the config preservePanel option
53253      * @return {Roo.ContentPanel} The panel that was removed
53254      */
53255     remove : function(panel, preservePanel){
53256         panel = this.getPanel(panel);
53257         if(!panel){
53258             return null;
53259         }
53260         var e = {};
53261         this.fireEvent("beforeremove", this, panel, e);
53262         if(e.cancel === true){
53263             return null;
53264         }
53265         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53266         var panelId = panel.getId();
53267         this.panels.removeKey(panelId);
53268         if(preservePanel){
53269             document.body.appendChild(panel.getEl().dom);
53270         }
53271         if(this.tabs){
53272             this.tabs.removeTab(panel.getEl().id);
53273         }else if (!preservePanel){
53274             this.bodyEl.dom.removeChild(panel.getEl().dom);
53275         }
53276         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53277             var p = this.panels.first();
53278             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53279             tempEl.appendChild(p.getEl().dom);
53280             this.bodyEl.update("");
53281             this.bodyEl.dom.appendChild(p.getEl().dom);
53282             tempEl = null;
53283             this.updateTitle(p.getTitle());
53284             this.tabs = null;
53285             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53286             this.setActivePanel(p);
53287         }
53288         panel.setRegion(null);
53289         if(this.activePanel == panel){
53290             this.activePanel = null;
53291         }
53292         if(this.config.autoDestroy !== false && preservePanel !== true){
53293             try{panel.destroy();}catch(e){}
53294         }
53295         this.fireEvent("panelremoved", this, panel);
53296         return panel;
53297     },
53298
53299     /**
53300      * Returns the TabPanel component used by this region
53301      * @return {Roo.TabPanel}
53302      */
53303     getTabs : function(){
53304         return this.tabs;
53305     },
53306
53307     createTool : function(parentEl, className){
53308         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53309             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53310         btn.addClassOnOver("x-layout-tools-button-over");
53311         return btn;
53312     }
53313 });/*
53314  * Based on:
53315  * Ext JS Library 1.1.1
53316  * Copyright(c) 2006-2007, Ext JS, LLC.
53317  *
53318  * Originally Released Under LGPL - original licence link has changed is not relivant.
53319  *
53320  * Fork - LGPL
53321  * <script type="text/javascript">
53322  */
53323  
53324
53325
53326 /**
53327  * @class Roo.SplitLayoutRegion
53328  * @extends Roo.LayoutRegion
53329  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53330  */
53331 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53332     this.cursor = cursor;
53333     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53334 };
53335
53336 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53337     splitTip : "Drag to resize.",
53338     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53339     useSplitTips : false,
53340
53341     applyConfig : function(config){
53342         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53343         if(config.split){
53344             if(!this.split){
53345                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53346                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53347                 /** The SplitBar for this region 
53348                 * @type Roo.SplitBar */
53349                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53350                 this.split.on("moved", this.onSplitMove, this);
53351                 this.split.useShim = config.useShim === true;
53352                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53353                 if(this.useSplitTips){
53354                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53355                 }
53356                 if(config.collapsible){
53357                     this.split.el.on("dblclick", this.collapse,  this);
53358                 }
53359             }
53360             if(typeof config.minSize != "undefined"){
53361                 this.split.minSize = config.minSize;
53362             }
53363             if(typeof config.maxSize != "undefined"){
53364                 this.split.maxSize = config.maxSize;
53365             }
53366             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53367                 this.hideSplitter();
53368             }
53369         }
53370     },
53371
53372     getHMaxSize : function(){
53373          var cmax = this.config.maxSize || 10000;
53374          var center = this.mgr.getRegion("center");
53375          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53376     },
53377
53378     getVMaxSize : function(){
53379          var cmax = this.config.maxSize || 10000;
53380          var center = this.mgr.getRegion("center");
53381          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53382     },
53383
53384     onSplitMove : function(split, newSize){
53385         this.fireEvent("resized", this, newSize);
53386     },
53387     
53388     /** 
53389      * Returns the {@link Roo.SplitBar} for this region.
53390      * @return {Roo.SplitBar}
53391      */
53392     getSplitBar : function(){
53393         return this.split;
53394     },
53395     
53396     hide : function(){
53397         this.hideSplitter();
53398         Roo.SplitLayoutRegion.superclass.hide.call(this);
53399     },
53400
53401     hideSplitter : function(){
53402         if(this.split){
53403             this.split.el.setLocation(-2000,-2000);
53404             this.split.el.hide();
53405         }
53406     },
53407
53408     show : function(){
53409         if(this.split){
53410             this.split.el.show();
53411         }
53412         Roo.SplitLayoutRegion.superclass.show.call(this);
53413     },
53414     
53415     beforeSlide: function(){
53416         if(Roo.isGecko){// firefox overflow auto bug workaround
53417             this.bodyEl.clip();
53418             if(this.tabs) {
53419                 this.tabs.bodyEl.clip();
53420             }
53421             if(this.activePanel){
53422                 this.activePanel.getEl().clip();
53423                 
53424                 if(this.activePanel.beforeSlide){
53425                     this.activePanel.beforeSlide();
53426                 }
53427             }
53428         }
53429     },
53430     
53431     afterSlide : function(){
53432         if(Roo.isGecko){// firefox overflow auto bug workaround
53433             this.bodyEl.unclip();
53434             if(this.tabs) {
53435                 this.tabs.bodyEl.unclip();
53436             }
53437             if(this.activePanel){
53438                 this.activePanel.getEl().unclip();
53439                 if(this.activePanel.afterSlide){
53440                     this.activePanel.afterSlide();
53441                 }
53442             }
53443         }
53444     },
53445
53446     initAutoHide : function(){
53447         if(this.autoHide !== false){
53448             if(!this.autoHideHd){
53449                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53450                 this.autoHideHd = {
53451                     "mouseout": function(e){
53452                         if(!e.within(this.el, true)){
53453                             st.delay(500);
53454                         }
53455                     },
53456                     "mouseover" : function(e){
53457                         st.cancel();
53458                     },
53459                     scope : this
53460                 };
53461             }
53462             this.el.on(this.autoHideHd);
53463         }
53464     },
53465
53466     clearAutoHide : function(){
53467         if(this.autoHide !== false){
53468             this.el.un("mouseout", this.autoHideHd.mouseout);
53469             this.el.un("mouseover", this.autoHideHd.mouseover);
53470         }
53471     },
53472
53473     clearMonitor : function(){
53474         Roo.get(document).un("click", this.slideInIf, this);
53475     },
53476
53477     // these names are backwards but not changed for compat
53478     slideOut : function(){
53479         if(this.isSlid || this.el.hasActiveFx()){
53480             return;
53481         }
53482         this.isSlid = true;
53483         if(this.collapseBtn){
53484             this.collapseBtn.hide();
53485         }
53486         this.closeBtnState = this.closeBtn.getStyle('display');
53487         this.closeBtn.hide();
53488         if(this.stickBtn){
53489             this.stickBtn.show();
53490         }
53491         this.el.show();
53492         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53493         this.beforeSlide();
53494         this.el.setStyle("z-index", 10001);
53495         this.el.slideIn(this.getSlideAnchor(), {
53496             callback: function(){
53497                 this.afterSlide();
53498                 this.initAutoHide();
53499                 Roo.get(document).on("click", this.slideInIf, this);
53500                 this.fireEvent("slideshow", this);
53501             },
53502             scope: this,
53503             block: true
53504         });
53505     },
53506
53507     afterSlideIn : function(){
53508         this.clearAutoHide();
53509         this.isSlid = false;
53510         this.clearMonitor();
53511         this.el.setStyle("z-index", "");
53512         if(this.collapseBtn){
53513             this.collapseBtn.show();
53514         }
53515         this.closeBtn.setStyle('display', this.closeBtnState);
53516         if(this.stickBtn){
53517             this.stickBtn.hide();
53518         }
53519         this.fireEvent("slidehide", this);
53520     },
53521
53522     slideIn : function(cb){
53523         if(!this.isSlid || this.el.hasActiveFx()){
53524             Roo.callback(cb);
53525             return;
53526         }
53527         this.isSlid = false;
53528         this.beforeSlide();
53529         this.el.slideOut(this.getSlideAnchor(), {
53530             callback: function(){
53531                 this.el.setLeftTop(-10000, -10000);
53532                 this.afterSlide();
53533                 this.afterSlideIn();
53534                 Roo.callback(cb);
53535             },
53536             scope: this,
53537             block: true
53538         });
53539     },
53540     
53541     slideInIf : function(e){
53542         if(!e.within(this.el)){
53543             this.slideIn();
53544         }
53545     },
53546
53547     animateCollapse : function(){
53548         this.beforeSlide();
53549         this.el.setStyle("z-index", 20000);
53550         var anchor = this.getSlideAnchor();
53551         this.el.slideOut(anchor, {
53552             callback : function(){
53553                 this.el.setStyle("z-index", "");
53554                 this.collapsedEl.slideIn(anchor, {duration:.3});
53555                 this.afterSlide();
53556                 this.el.setLocation(-10000,-10000);
53557                 this.el.hide();
53558                 this.fireEvent("collapsed", this);
53559             },
53560             scope: this,
53561             block: true
53562         });
53563     },
53564
53565     animateExpand : function(){
53566         this.beforeSlide();
53567         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53568         this.el.setStyle("z-index", 20000);
53569         this.collapsedEl.hide({
53570             duration:.1
53571         });
53572         this.el.slideIn(this.getSlideAnchor(), {
53573             callback : function(){
53574                 this.el.setStyle("z-index", "");
53575                 this.afterSlide();
53576                 if(this.split){
53577                     this.split.el.show();
53578                 }
53579                 this.fireEvent("invalidated", this);
53580                 this.fireEvent("expanded", this);
53581             },
53582             scope: this,
53583             block: true
53584         });
53585     },
53586
53587     anchors : {
53588         "west" : "left",
53589         "east" : "right",
53590         "north" : "top",
53591         "south" : "bottom"
53592     },
53593
53594     sanchors : {
53595         "west" : "l",
53596         "east" : "r",
53597         "north" : "t",
53598         "south" : "b"
53599     },
53600
53601     canchors : {
53602         "west" : "tl-tr",
53603         "east" : "tr-tl",
53604         "north" : "tl-bl",
53605         "south" : "bl-tl"
53606     },
53607
53608     getAnchor : function(){
53609         return this.anchors[this.position];
53610     },
53611
53612     getCollapseAnchor : function(){
53613         return this.canchors[this.position];
53614     },
53615
53616     getSlideAnchor : function(){
53617         return this.sanchors[this.position];
53618     },
53619
53620     getAlignAdj : function(){
53621         var cm = this.cmargins;
53622         switch(this.position){
53623             case "west":
53624                 return [0, 0];
53625             break;
53626             case "east":
53627                 return [0, 0];
53628             break;
53629             case "north":
53630                 return [0, 0];
53631             break;
53632             case "south":
53633                 return [0, 0];
53634             break;
53635         }
53636     },
53637
53638     getExpandAdj : function(){
53639         var c = this.collapsedEl, cm = this.cmargins;
53640         switch(this.position){
53641             case "west":
53642                 return [-(cm.right+c.getWidth()+cm.left), 0];
53643             break;
53644             case "east":
53645                 return [cm.right+c.getWidth()+cm.left, 0];
53646             break;
53647             case "north":
53648                 return [0, -(cm.top+cm.bottom+c.getHeight())];
53649             break;
53650             case "south":
53651                 return [0, cm.top+cm.bottom+c.getHeight()];
53652             break;
53653         }
53654     }
53655 });/*
53656  * Based on:
53657  * Ext JS Library 1.1.1
53658  * Copyright(c) 2006-2007, Ext JS, LLC.
53659  *
53660  * Originally Released Under LGPL - original licence link has changed is not relivant.
53661  *
53662  * Fork - LGPL
53663  * <script type="text/javascript">
53664  */
53665 /*
53666  * These classes are private internal classes
53667  */
53668 Roo.CenterLayoutRegion = function(mgr, config){
53669     Roo.LayoutRegion.call(this, mgr, config, "center");
53670     this.visible = true;
53671     this.minWidth = config.minWidth || 20;
53672     this.minHeight = config.minHeight || 20;
53673 };
53674
53675 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
53676     hide : function(){
53677         // center panel can't be hidden
53678     },
53679     
53680     show : function(){
53681         // center panel can't be hidden
53682     },
53683     
53684     getMinWidth: function(){
53685         return this.minWidth;
53686     },
53687     
53688     getMinHeight: function(){
53689         return this.minHeight;
53690     }
53691 });
53692
53693
53694 Roo.NorthLayoutRegion = function(mgr, config){
53695     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
53696     if(this.split){
53697         this.split.placement = Roo.SplitBar.TOP;
53698         this.split.orientation = Roo.SplitBar.VERTICAL;
53699         this.split.el.addClass("x-layout-split-v");
53700     }
53701     var size = config.initialSize || config.height;
53702     if(typeof size != "undefined"){
53703         this.el.setHeight(size);
53704     }
53705 };
53706 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
53707     orientation: Roo.SplitBar.VERTICAL,
53708     getBox : function(){
53709         if(this.collapsed){
53710             return this.collapsedEl.getBox();
53711         }
53712         var box = this.el.getBox();
53713         if(this.split){
53714             box.height += this.split.el.getHeight();
53715         }
53716         return box;
53717     },
53718     
53719     updateBox : function(box){
53720         if(this.split && !this.collapsed){
53721             box.height -= this.split.el.getHeight();
53722             this.split.el.setLeft(box.x);
53723             this.split.el.setTop(box.y+box.height);
53724             this.split.el.setWidth(box.width);
53725         }
53726         if(this.collapsed){
53727             this.updateBody(box.width, null);
53728         }
53729         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53730     }
53731 });
53732
53733 Roo.SouthLayoutRegion = function(mgr, config){
53734     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
53735     if(this.split){
53736         this.split.placement = Roo.SplitBar.BOTTOM;
53737         this.split.orientation = Roo.SplitBar.VERTICAL;
53738         this.split.el.addClass("x-layout-split-v");
53739     }
53740     var size = config.initialSize || config.height;
53741     if(typeof size != "undefined"){
53742         this.el.setHeight(size);
53743     }
53744 };
53745 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
53746     orientation: Roo.SplitBar.VERTICAL,
53747     getBox : function(){
53748         if(this.collapsed){
53749             return this.collapsedEl.getBox();
53750         }
53751         var box = this.el.getBox();
53752         if(this.split){
53753             var sh = this.split.el.getHeight();
53754             box.height += sh;
53755             box.y -= sh;
53756         }
53757         return box;
53758     },
53759     
53760     updateBox : function(box){
53761         if(this.split && !this.collapsed){
53762             var sh = this.split.el.getHeight();
53763             box.height -= sh;
53764             box.y += sh;
53765             this.split.el.setLeft(box.x);
53766             this.split.el.setTop(box.y-sh);
53767             this.split.el.setWidth(box.width);
53768         }
53769         if(this.collapsed){
53770             this.updateBody(box.width, null);
53771         }
53772         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53773     }
53774 });
53775
53776 Roo.EastLayoutRegion = function(mgr, config){
53777     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
53778     if(this.split){
53779         this.split.placement = Roo.SplitBar.RIGHT;
53780         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53781         this.split.el.addClass("x-layout-split-h");
53782     }
53783     var size = config.initialSize || config.width;
53784     if(typeof size != "undefined"){
53785         this.el.setWidth(size);
53786     }
53787 };
53788 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
53789     orientation: Roo.SplitBar.HORIZONTAL,
53790     getBox : function(){
53791         if(this.collapsed){
53792             return this.collapsedEl.getBox();
53793         }
53794         var box = this.el.getBox();
53795         if(this.split){
53796             var sw = this.split.el.getWidth();
53797             box.width += sw;
53798             box.x -= sw;
53799         }
53800         return box;
53801     },
53802
53803     updateBox : function(box){
53804         if(this.split && !this.collapsed){
53805             var sw = this.split.el.getWidth();
53806             box.width -= sw;
53807             this.split.el.setLeft(box.x);
53808             this.split.el.setTop(box.y);
53809             this.split.el.setHeight(box.height);
53810             box.x += sw;
53811         }
53812         if(this.collapsed){
53813             this.updateBody(null, box.height);
53814         }
53815         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53816     }
53817 });
53818
53819 Roo.WestLayoutRegion = function(mgr, config){
53820     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
53821     if(this.split){
53822         this.split.placement = Roo.SplitBar.LEFT;
53823         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53824         this.split.el.addClass("x-layout-split-h");
53825     }
53826     var size = config.initialSize || config.width;
53827     if(typeof size != "undefined"){
53828         this.el.setWidth(size);
53829     }
53830 };
53831 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
53832     orientation: Roo.SplitBar.HORIZONTAL,
53833     getBox : function(){
53834         if(this.collapsed){
53835             return this.collapsedEl.getBox();
53836         }
53837         var box = this.el.getBox();
53838         if(this.split){
53839             box.width += this.split.el.getWidth();
53840         }
53841         return box;
53842     },
53843     
53844     updateBox : function(box){
53845         if(this.split && !this.collapsed){
53846             var sw = this.split.el.getWidth();
53847             box.width -= sw;
53848             this.split.el.setLeft(box.x+box.width);
53849             this.split.el.setTop(box.y);
53850             this.split.el.setHeight(box.height);
53851         }
53852         if(this.collapsed){
53853             this.updateBody(null, box.height);
53854         }
53855         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53856     }
53857 });
53858 /*
53859  * Based on:
53860  * Ext JS Library 1.1.1
53861  * Copyright(c) 2006-2007, Ext JS, LLC.
53862  *
53863  * Originally Released Under LGPL - original licence link has changed is not relivant.
53864  *
53865  * Fork - LGPL
53866  * <script type="text/javascript">
53867  */
53868  
53869  
53870 /*
53871  * Private internal class for reading and applying state
53872  */
53873 Roo.LayoutStateManager = function(layout){
53874      // default empty state
53875      this.state = {
53876         north: {},
53877         south: {},
53878         east: {},
53879         west: {}       
53880     };
53881 };
53882
53883 Roo.LayoutStateManager.prototype = {
53884     init : function(layout, provider){
53885         this.provider = provider;
53886         var state = provider.get(layout.id+"-layout-state");
53887         if(state){
53888             var wasUpdating = layout.isUpdating();
53889             if(!wasUpdating){
53890                 layout.beginUpdate();
53891             }
53892             for(var key in state){
53893                 if(typeof state[key] != "function"){
53894                     var rstate = state[key];
53895                     var r = layout.getRegion(key);
53896                     if(r && rstate){
53897                         if(rstate.size){
53898                             r.resizeTo(rstate.size);
53899                         }
53900                         if(rstate.collapsed == true){
53901                             r.collapse(true);
53902                         }else{
53903                             r.expand(null, true);
53904                         }
53905                     }
53906                 }
53907             }
53908             if(!wasUpdating){
53909                 layout.endUpdate();
53910             }
53911             this.state = state; 
53912         }
53913         this.layout = layout;
53914         layout.on("regionresized", this.onRegionResized, this);
53915         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53916         layout.on("regionexpanded", this.onRegionExpanded, this);
53917     },
53918     
53919     storeState : function(){
53920         this.provider.set(this.layout.id+"-layout-state", this.state);
53921     },
53922     
53923     onRegionResized : function(region, newSize){
53924         this.state[region.getPosition()].size = newSize;
53925         this.storeState();
53926     },
53927     
53928     onRegionCollapsed : function(region){
53929         this.state[region.getPosition()].collapsed = true;
53930         this.storeState();
53931     },
53932     
53933     onRegionExpanded : function(region){
53934         this.state[region.getPosition()].collapsed = false;
53935         this.storeState();
53936     }
53937 };/*
53938  * Based on:
53939  * Ext JS Library 1.1.1
53940  * Copyright(c) 2006-2007, Ext JS, LLC.
53941  *
53942  * Originally Released Under LGPL - original licence link has changed is not relivant.
53943  *
53944  * Fork - LGPL
53945  * <script type="text/javascript">
53946  */
53947 /**
53948  * @class Roo.ContentPanel
53949  * @extends Roo.util.Observable
53950  * A basic ContentPanel element.
53951  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53952  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53953  * @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
53954  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53955  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53956  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53957  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53958  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53959  * @cfg {String} title          The title for this panel
53960  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53961  * @cfg {String} url            Calls {@link #setUrl} with this value
53962  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53963  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53964  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53965  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53966
53967  * @constructor
53968  * Create a new ContentPanel.
53969  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53970  * @param {String/Object} config A string to set only the title or a config object
53971  * @param {String} content (optional) Set the HTML content for this panel
53972  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53973  */
53974 Roo.ContentPanel = function(el, config, content){
53975     
53976      
53977     /*
53978     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53979         config = el;
53980         el = Roo.id();
53981     }
53982     if (config && config.parentLayout) { 
53983         el = config.parentLayout.el.createChild(); 
53984     }
53985     */
53986     if(el.autoCreate){ // xtype is available if this is called from factory
53987         config = el;
53988         el = Roo.id();
53989     }
53990     this.el = Roo.get(el);
53991     if(!this.el && config && config.autoCreate){
53992         if(typeof config.autoCreate == "object"){
53993             if(!config.autoCreate.id){
53994                 config.autoCreate.id = config.id||el;
53995             }
53996             this.el = Roo.DomHelper.append(document.body,
53997                         config.autoCreate, true);
53998         }else{
53999             this.el = Roo.DomHelper.append(document.body,
54000                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54001         }
54002     }
54003     this.closable = false;
54004     this.loaded = false;
54005     this.active = false;
54006     if(typeof config == "string"){
54007         this.title = config;
54008     }else{
54009         Roo.apply(this, config);
54010     }
54011     
54012     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54013         this.wrapEl = this.el.wrap();
54014         this.toolbar.container = this.el.insertSibling(false, 'before');
54015         this.toolbar = new Roo.Toolbar(this.toolbar);
54016     }
54017     
54018     // xtype created footer. - not sure if will work as we normally have to render first..
54019     if (this.footer && !this.footer.el && this.footer.xtype) {
54020         if (!this.wrapEl) {
54021             this.wrapEl = this.el.wrap();
54022         }
54023     
54024         this.footer.container = this.wrapEl.createChild();
54025          
54026         this.footer = Roo.factory(this.footer, Roo);
54027         
54028     }
54029     
54030     if(this.resizeEl){
54031         this.resizeEl = Roo.get(this.resizeEl, true);
54032     }else{
54033         this.resizeEl = this.el;
54034     }
54035     // handle view.xtype
54036     
54037  
54038     
54039     
54040     this.addEvents({
54041         /**
54042          * @event activate
54043          * Fires when this panel is activated. 
54044          * @param {Roo.ContentPanel} this
54045          */
54046         "activate" : true,
54047         /**
54048          * @event deactivate
54049          * Fires when this panel is activated. 
54050          * @param {Roo.ContentPanel} this
54051          */
54052         "deactivate" : true,
54053
54054         /**
54055          * @event resize
54056          * Fires when this panel is resized if fitToFrame is true.
54057          * @param {Roo.ContentPanel} this
54058          * @param {Number} width The width after any component adjustments
54059          * @param {Number} height The height after any component adjustments
54060          */
54061         "resize" : true,
54062         
54063          /**
54064          * @event render
54065          * Fires when this tab is created
54066          * @param {Roo.ContentPanel} this
54067          */
54068         "render" : true
54069          
54070         
54071     });
54072     
54073
54074     
54075     
54076     if(this.autoScroll){
54077         this.resizeEl.setStyle("overflow", "auto");
54078     } else {
54079         // fix randome scrolling
54080         this.el.on('scroll', function() {
54081             Roo.log('fix random scolling');
54082             this.scrollTo('top',0); 
54083         });
54084     }
54085     content = content || this.content;
54086     if(content){
54087         this.setContent(content);
54088     }
54089     if(config && config.url){
54090         this.setUrl(this.url, this.params, this.loadOnce);
54091     }
54092     
54093     
54094     
54095     Roo.ContentPanel.superclass.constructor.call(this);
54096     
54097     if (this.view && typeof(this.view.xtype) != 'undefined') {
54098         this.view.el = this.el.appendChild(document.createElement("div"));
54099         this.view = Roo.factory(this.view); 
54100         this.view.render  &&  this.view.render(false, '');  
54101     }
54102     
54103     
54104     this.fireEvent('render', this);
54105 };
54106
54107 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54108     tabTip:'',
54109     setRegion : function(region){
54110         this.region = region;
54111         if(region){
54112            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54113         }else{
54114            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54115         } 
54116     },
54117     
54118     /**
54119      * Returns the toolbar for this Panel if one was configured. 
54120      * @return {Roo.Toolbar} 
54121      */
54122     getToolbar : function(){
54123         return this.toolbar;
54124     },
54125     
54126     setActiveState : function(active){
54127         this.active = active;
54128         if(!active){
54129             this.fireEvent("deactivate", this);
54130         }else{
54131             this.fireEvent("activate", this);
54132         }
54133     },
54134     /**
54135      * Updates this panel's element
54136      * @param {String} content The new content
54137      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54138     */
54139     setContent : function(content, loadScripts){
54140         this.el.update(content, loadScripts);
54141     },
54142
54143     ignoreResize : function(w, h){
54144         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54145             return true;
54146         }else{
54147             this.lastSize = {width: w, height: h};
54148             return false;
54149         }
54150     },
54151     /**
54152      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54153      * @return {Roo.UpdateManager} The UpdateManager
54154      */
54155     getUpdateManager : function(){
54156         return this.el.getUpdateManager();
54157     },
54158      /**
54159      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54160      * @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:
54161 <pre><code>
54162 panel.load({
54163     url: "your-url.php",
54164     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54165     callback: yourFunction,
54166     scope: yourObject, //(optional scope)
54167     discardUrl: false,
54168     nocache: false,
54169     text: "Loading...",
54170     timeout: 30,
54171     scripts: false
54172 });
54173 </code></pre>
54174      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54175      * 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.
54176      * @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}
54177      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54178      * @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.
54179      * @return {Roo.ContentPanel} this
54180      */
54181     load : function(){
54182         var um = this.el.getUpdateManager();
54183         um.update.apply(um, arguments);
54184         return this;
54185     },
54186
54187
54188     /**
54189      * 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.
54190      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54191      * @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)
54192      * @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)
54193      * @return {Roo.UpdateManager} The UpdateManager
54194      */
54195     setUrl : function(url, params, loadOnce){
54196         if(this.refreshDelegate){
54197             this.removeListener("activate", this.refreshDelegate);
54198         }
54199         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54200         this.on("activate", this.refreshDelegate);
54201         return this.el.getUpdateManager();
54202     },
54203     
54204     _handleRefresh : function(url, params, loadOnce){
54205         if(!loadOnce || !this.loaded){
54206             var updater = this.el.getUpdateManager();
54207             updater.update(url, params, this._setLoaded.createDelegate(this));
54208         }
54209     },
54210     
54211     _setLoaded : function(){
54212         this.loaded = true;
54213     }, 
54214     
54215     /**
54216      * Returns this panel's id
54217      * @return {String} 
54218      */
54219     getId : function(){
54220         return this.el.id;
54221     },
54222     
54223     /** 
54224      * Returns this panel's element - used by regiosn to add.
54225      * @return {Roo.Element} 
54226      */
54227     getEl : function(){
54228         return this.wrapEl || this.el;
54229     },
54230     
54231     adjustForComponents : function(width, height)
54232     {
54233         //Roo.log('adjustForComponents ');
54234         if(this.resizeEl != this.el){
54235             width -= this.el.getFrameWidth('lr');
54236             height -= this.el.getFrameWidth('tb');
54237         }
54238         if(this.toolbar){
54239             var te = this.toolbar.getEl();
54240             height -= te.getHeight();
54241             te.setWidth(width);
54242         }
54243         if(this.footer){
54244             var te = this.footer.getEl();
54245             //Roo.log("footer:" + te.getHeight());
54246             
54247             height -= te.getHeight();
54248             te.setWidth(width);
54249         }
54250         
54251         
54252         if(this.adjustments){
54253             width += this.adjustments[0];
54254             height += this.adjustments[1];
54255         }
54256         return {"width": width, "height": height};
54257     },
54258     
54259     setSize : function(width, height){
54260         if(this.fitToFrame && !this.ignoreResize(width, height)){
54261             if(this.fitContainer && this.resizeEl != this.el){
54262                 this.el.setSize(width, height);
54263             }
54264             var size = this.adjustForComponents(width, height);
54265             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54266             this.fireEvent('resize', this, size.width, size.height);
54267         }
54268     },
54269     
54270     /**
54271      * Returns this panel's title
54272      * @return {String} 
54273      */
54274     getTitle : function(){
54275         return this.title;
54276     },
54277     
54278     /**
54279      * Set this panel's title
54280      * @param {String} title
54281      */
54282     setTitle : function(title){
54283         this.title = title;
54284         if(this.region){
54285             this.region.updatePanelTitle(this, title);
54286         }
54287     },
54288     
54289     /**
54290      * Returns true is this panel was configured to be closable
54291      * @return {Boolean} 
54292      */
54293     isClosable : function(){
54294         return this.closable;
54295     },
54296     
54297     beforeSlide : function(){
54298         this.el.clip();
54299         this.resizeEl.clip();
54300     },
54301     
54302     afterSlide : function(){
54303         this.el.unclip();
54304         this.resizeEl.unclip();
54305     },
54306     
54307     /**
54308      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54309      *   Will fail silently if the {@link #setUrl} method has not been called.
54310      *   This does not activate the panel, just updates its content.
54311      */
54312     refresh : function(){
54313         if(this.refreshDelegate){
54314            this.loaded = false;
54315            this.refreshDelegate();
54316         }
54317     },
54318     
54319     /**
54320      * Destroys this panel
54321      */
54322     destroy : function(){
54323         this.el.removeAllListeners();
54324         var tempEl = document.createElement("span");
54325         tempEl.appendChild(this.el.dom);
54326         tempEl.innerHTML = "";
54327         this.el.remove();
54328         this.el = null;
54329     },
54330     
54331     /**
54332      * form - if the content panel contains a form - this is a reference to it.
54333      * @type {Roo.form.Form}
54334      */
54335     form : false,
54336     /**
54337      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54338      *    This contains a reference to it.
54339      * @type {Roo.View}
54340      */
54341     view : false,
54342     
54343       /**
54344      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54345      * <pre><code>
54346
54347 layout.addxtype({
54348        xtype : 'Form',
54349        items: [ .... ]
54350    }
54351 );
54352
54353 </code></pre>
54354      * @param {Object} cfg Xtype definition of item to add.
54355      */
54356     
54357     addxtype : function(cfg) {
54358         // add form..
54359         if (cfg.xtype.match(/^Form$/)) {
54360             
54361             var el;
54362             //if (this.footer) {
54363             //    el = this.footer.container.insertSibling(false, 'before');
54364             //} else {
54365                 el = this.el.createChild();
54366             //}
54367
54368             this.form = new  Roo.form.Form(cfg);
54369             
54370             
54371             if ( this.form.allItems.length) {
54372                 this.form.render(el.dom);
54373             }
54374             return this.form;
54375         }
54376         // should only have one of theses..
54377         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54378             // views.. should not be just added - used named prop 'view''
54379             
54380             cfg.el = this.el.appendChild(document.createElement("div"));
54381             // factory?
54382             
54383             var ret = new Roo.factory(cfg);
54384              
54385              ret.render && ret.render(false, ''); // render blank..
54386             this.view = ret;
54387             return ret;
54388         }
54389         return false;
54390     }
54391 });
54392
54393 /**
54394  * @class Roo.GridPanel
54395  * @extends Roo.ContentPanel
54396  * @constructor
54397  * Create a new GridPanel.
54398  * @param {Roo.grid.Grid} grid The grid for this panel
54399  * @param {String/Object} config A string to set only the panel's title, or a config object
54400  */
54401 Roo.GridPanel = function(grid, config){
54402     
54403   
54404     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54405         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54406         
54407     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54408     
54409     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54410     
54411     if(this.toolbar){
54412         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54413     }
54414     // xtype created footer. - not sure if will work as we normally have to render first..
54415     if (this.footer && !this.footer.el && this.footer.xtype) {
54416         
54417         this.footer.container = this.grid.getView().getFooterPanel(true);
54418         this.footer.dataSource = this.grid.dataSource;
54419         this.footer = Roo.factory(this.footer, Roo);
54420         
54421     }
54422     
54423     grid.monitorWindowResize = false; // turn off autosizing
54424     grid.autoHeight = false;
54425     grid.autoWidth = false;
54426     this.grid = grid;
54427     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54428 };
54429
54430 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54431     getId : function(){
54432         return this.grid.id;
54433     },
54434     
54435     /**
54436      * Returns the grid for this panel
54437      * @return {Roo.grid.Grid} 
54438      */
54439     getGrid : function(){
54440         return this.grid;    
54441     },
54442     
54443     setSize : function(width, height){
54444         if(!this.ignoreResize(width, height)){
54445             var grid = this.grid;
54446             var size = this.adjustForComponents(width, height);
54447             grid.getGridEl().setSize(size.width, size.height);
54448             grid.autoSize();
54449         }
54450     },
54451     
54452     beforeSlide : function(){
54453         this.grid.getView().scroller.clip();
54454     },
54455     
54456     afterSlide : function(){
54457         this.grid.getView().scroller.unclip();
54458     },
54459     
54460     destroy : function(){
54461         this.grid.destroy();
54462         delete this.grid;
54463         Roo.GridPanel.superclass.destroy.call(this); 
54464     }
54465 });
54466
54467
54468 /**
54469  * @class Roo.NestedLayoutPanel
54470  * @extends Roo.ContentPanel
54471  * @constructor
54472  * Create a new NestedLayoutPanel.
54473  * 
54474  * 
54475  * @param {Roo.BorderLayout} layout The layout for this panel
54476  * @param {String/Object} config A string to set only the title or a config object
54477  */
54478 Roo.NestedLayoutPanel = function(layout, config)
54479 {
54480     // construct with only one argument..
54481     /* FIXME - implement nicer consturctors
54482     if (layout.layout) {
54483         config = layout;
54484         layout = config.layout;
54485         delete config.layout;
54486     }
54487     if (layout.xtype && !layout.getEl) {
54488         // then layout needs constructing..
54489         layout = Roo.factory(layout, Roo);
54490     }
54491     */
54492     
54493     
54494     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54495     
54496     layout.monitorWindowResize = false; // turn off autosizing
54497     this.layout = layout;
54498     this.layout.getEl().addClass("x-layout-nested-layout");
54499     
54500     
54501     
54502     
54503 };
54504
54505 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54506
54507     setSize : function(width, height){
54508         if(!this.ignoreResize(width, height)){
54509             var size = this.adjustForComponents(width, height);
54510             var el = this.layout.getEl();
54511             el.setSize(size.width, size.height);
54512             var touch = el.dom.offsetWidth;
54513             this.layout.layout();
54514             // ie requires a double layout on the first pass
54515             if(Roo.isIE && !this.initialized){
54516                 this.initialized = true;
54517                 this.layout.layout();
54518             }
54519         }
54520     },
54521     
54522     // activate all subpanels if not currently active..
54523     
54524     setActiveState : function(active){
54525         this.active = active;
54526         if(!active){
54527             this.fireEvent("deactivate", this);
54528             return;
54529         }
54530         
54531         this.fireEvent("activate", this);
54532         // not sure if this should happen before or after..
54533         if (!this.layout) {
54534             return; // should not happen..
54535         }
54536         var reg = false;
54537         for (var r in this.layout.regions) {
54538             reg = this.layout.getRegion(r);
54539             if (reg.getActivePanel()) {
54540                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54541                 reg.setActivePanel(reg.getActivePanel());
54542                 continue;
54543             }
54544             if (!reg.panels.length) {
54545                 continue;
54546             }
54547             reg.showPanel(reg.getPanel(0));
54548         }
54549         
54550         
54551         
54552         
54553     },
54554     
54555     /**
54556      * Returns the nested BorderLayout for this panel
54557      * @return {Roo.BorderLayout} 
54558      */
54559     getLayout : function(){
54560         return this.layout;
54561     },
54562     
54563      /**
54564      * Adds a xtype elements to the layout of the nested panel
54565      * <pre><code>
54566
54567 panel.addxtype({
54568        xtype : 'ContentPanel',
54569        region: 'west',
54570        items: [ .... ]
54571    }
54572 );
54573
54574 panel.addxtype({
54575         xtype : 'NestedLayoutPanel',
54576         region: 'west',
54577         layout: {
54578            center: { },
54579            west: { }   
54580         },
54581         items : [ ... list of content panels or nested layout panels.. ]
54582    }
54583 );
54584 </code></pre>
54585      * @param {Object} cfg Xtype definition of item to add.
54586      */
54587     addxtype : function(cfg) {
54588         return this.layout.addxtype(cfg);
54589     
54590     }
54591 });
54592
54593 Roo.ScrollPanel = function(el, config, content){
54594     config = config || {};
54595     config.fitToFrame = true;
54596     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
54597     
54598     this.el.dom.style.overflow = "hidden";
54599     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
54600     this.el.removeClass("x-layout-inactive-content");
54601     this.el.on("mousewheel", this.onWheel, this);
54602
54603     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
54604     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
54605     up.unselectable(); down.unselectable();
54606     up.on("click", this.scrollUp, this);
54607     down.on("click", this.scrollDown, this);
54608     up.addClassOnOver("x-scroller-btn-over");
54609     down.addClassOnOver("x-scroller-btn-over");
54610     up.addClassOnClick("x-scroller-btn-click");
54611     down.addClassOnClick("x-scroller-btn-click");
54612     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
54613
54614     this.resizeEl = this.el;
54615     this.el = wrap; this.up = up; this.down = down;
54616 };
54617
54618 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
54619     increment : 100,
54620     wheelIncrement : 5,
54621     scrollUp : function(){
54622         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
54623     },
54624
54625     scrollDown : function(){
54626         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
54627     },
54628
54629     afterScroll : function(){
54630         var el = this.resizeEl;
54631         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
54632         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54633         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54634     },
54635
54636     setSize : function(){
54637         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
54638         this.afterScroll();
54639     },
54640
54641     onWheel : function(e){
54642         var d = e.getWheelDelta();
54643         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
54644         this.afterScroll();
54645         e.stopEvent();
54646     },
54647
54648     setContent : function(content, loadScripts){
54649         this.resizeEl.update(content, loadScripts);
54650     }
54651
54652 });
54653
54654
54655
54656
54657
54658
54659
54660
54661
54662 /**
54663  * @class Roo.TreePanel
54664  * @extends Roo.ContentPanel
54665  * @constructor
54666  * Create a new TreePanel. - defaults to fit/scoll contents.
54667  * @param {String/Object} config A string to set only the panel's title, or a config object
54668  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
54669  */
54670 Roo.TreePanel = function(config){
54671     var el = config.el;
54672     var tree = config.tree;
54673     delete config.tree; 
54674     delete config.el; // hopefull!
54675     
54676     // wrapper for IE7 strict & safari scroll issue
54677     
54678     var treeEl = el.createChild();
54679     config.resizeEl = treeEl;
54680     
54681     
54682     
54683     Roo.TreePanel.superclass.constructor.call(this, el, config);
54684  
54685  
54686     this.tree = new Roo.tree.TreePanel(treeEl , tree);
54687     //console.log(tree);
54688     this.on('activate', function()
54689     {
54690         if (this.tree.rendered) {
54691             return;
54692         }
54693         //console.log('render tree');
54694         this.tree.render();
54695     });
54696     // this should not be needed.. - it's actually the 'el' that resizes?
54697     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
54698     
54699     //this.on('resize',  function (cp, w, h) {
54700     //        this.tree.innerCt.setWidth(w);
54701     //        this.tree.innerCt.setHeight(h);
54702     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
54703     //});
54704
54705         
54706     
54707 };
54708
54709 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
54710     fitToFrame : true,
54711     autoScroll : true
54712 });
54713
54714
54715
54716
54717
54718
54719
54720
54721
54722
54723
54724 /*
54725  * Based on:
54726  * Ext JS Library 1.1.1
54727  * Copyright(c) 2006-2007, Ext JS, LLC.
54728  *
54729  * Originally Released Under LGPL - original licence link has changed is not relivant.
54730  *
54731  * Fork - LGPL
54732  * <script type="text/javascript">
54733  */
54734  
54735
54736 /**
54737  * @class Roo.ReaderLayout
54738  * @extends Roo.BorderLayout
54739  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
54740  * center region containing two nested regions (a top one for a list view and one for item preview below),
54741  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
54742  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
54743  * expedites the setup of the overall layout and regions for this common application style.
54744  * Example:
54745  <pre><code>
54746 var reader = new Roo.ReaderLayout();
54747 var CP = Roo.ContentPanel;  // shortcut for adding
54748
54749 reader.beginUpdate();
54750 reader.add("north", new CP("north", "North"));
54751 reader.add("west", new CP("west", {title: "West"}));
54752 reader.add("east", new CP("east", {title: "East"}));
54753
54754 reader.regions.listView.add(new CP("listView", "List"));
54755 reader.regions.preview.add(new CP("preview", "Preview"));
54756 reader.endUpdate();
54757 </code></pre>
54758 * @constructor
54759 * Create a new ReaderLayout
54760 * @param {Object} config Configuration options
54761 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
54762 * document.body if omitted)
54763 */
54764 Roo.ReaderLayout = function(config, renderTo){
54765     var c = config || {size:{}};
54766     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
54767         north: c.north !== false ? Roo.apply({
54768             split:false,
54769             initialSize: 32,
54770             titlebar: false
54771         }, c.north) : false,
54772         west: c.west !== false ? Roo.apply({
54773             split:true,
54774             initialSize: 200,
54775             minSize: 175,
54776             maxSize: 400,
54777             titlebar: true,
54778             collapsible: true,
54779             animate: true,
54780             margins:{left:5,right:0,bottom:5,top:5},
54781             cmargins:{left:5,right:5,bottom:5,top:5}
54782         }, c.west) : false,
54783         east: c.east !== false ? Roo.apply({
54784             split:true,
54785             initialSize: 200,
54786             minSize: 175,
54787             maxSize: 400,
54788             titlebar: true,
54789             collapsible: true,
54790             animate: true,
54791             margins:{left:0,right:5,bottom:5,top:5},
54792             cmargins:{left:5,right:5,bottom:5,top:5}
54793         }, c.east) : false,
54794         center: Roo.apply({
54795             tabPosition: 'top',
54796             autoScroll:false,
54797             closeOnTab: true,
54798             titlebar:false,
54799             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
54800         }, c.center)
54801     });
54802
54803     this.el.addClass('x-reader');
54804
54805     this.beginUpdate();
54806
54807     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
54808         south: c.preview !== false ? Roo.apply({
54809             split:true,
54810             initialSize: 200,
54811             minSize: 100,
54812             autoScroll:true,
54813             collapsible:true,
54814             titlebar: true,
54815             cmargins:{top:5,left:0, right:0, bottom:0}
54816         }, c.preview) : false,
54817         center: Roo.apply({
54818             autoScroll:false,
54819             titlebar:false,
54820             minHeight:200
54821         }, c.listView)
54822     });
54823     this.add('center', new Roo.NestedLayoutPanel(inner,
54824             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
54825
54826     this.endUpdate();
54827
54828     this.regions.preview = inner.getRegion('south');
54829     this.regions.listView = inner.getRegion('center');
54830 };
54831
54832 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
54833  * Based on:
54834  * Ext JS Library 1.1.1
54835  * Copyright(c) 2006-2007, Ext JS, LLC.
54836  *
54837  * Originally Released Under LGPL - original licence link has changed is not relivant.
54838  *
54839  * Fork - LGPL
54840  * <script type="text/javascript">
54841  */
54842  
54843 /**
54844  * @class Roo.grid.Grid
54845  * @extends Roo.util.Observable
54846  * This class represents the primary interface of a component based grid control.
54847  * <br><br>Usage:<pre><code>
54848  var grid = new Roo.grid.Grid("my-container-id", {
54849      ds: myDataStore,
54850      cm: myColModel,
54851      selModel: mySelectionModel,
54852      autoSizeColumns: true,
54853      monitorWindowResize: false,
54854      trackMouseOver: true
54855  });
54856  // set any options
54857  grid.render();
54858  * </code></pre>
54859  * <b>Common Problems:</b><br/>
54860  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
54861  * element will correct this<br/>
54862  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
54863  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
54864  * are unpredictable.<br/>
54865  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
54866  * grid to calculate dimensions/offsets.<br/>
54867   * @constructor
54868  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54869  * The container MUST have some type of size defined for the grid to fill. The container will be
54870  * automatically set to position relative if it isn't already.
54871  * @param {Object} config A config object that sets properties on this grid.
54872  */
54873 Roo.grid.Grid = function(container, config){
54874         // initialize the container
54875         this.container = Roo.get(container);
54876         this.container.update("");
54877         this.container.setStyle("overflow", "hidden");
54878     this.container.addClass('x-grid-container');
54879
54880     this.id = this.container.id;
54881
54882     Roo.apply(this, config);
54883     // check and correct shorthanded configs
54884     if(this.ds){
54885         this.dataSource = this.ds;
54886         delete this.ds;
54887     }
54888     if(this.cm){
54889         this.colModel = this.cm;
54890         delete this.cm;
54891     }
54892     if(this.sm){
54893         this.selModel = this.sm;
54894         delete this.sm;
54895     }
54896
54897     if (this.selModel) {
54898         this.selModel = Roo.factory(this.selModel, Roo.grid);
54899         this.sm = this.selModel;
54900         this.sm.xmodule = this.xmodule || false;
54901     }
54902     if (typeof(this.colModel.config) == 'undefined') {
54903         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54904         this.cm = this.colModel;
54905         this.cm.xmodule = this.xmodule || false;
54906     }
54907     if (this.dataSource) {
54908         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54909         this.ds = this.dataSource;
54910         this.ds.xmodule = this.xmodule || false;
54911          
54912     }
54913     
54914     
54915     
54916     if(this.width){
54917         this.container.setWidth(this.width);
54918     }
54919
54920     if(this.height){
54921         this.container.setHeight(this.height);
54922     }
54923     /** @private */
54924         this.addEvents({
54925         // raw events
54926         /**
54927          * @event click
54928          * The raw click event for the entire grid.
54929          * @param {Roo.EventObject} e
54930          */
54931         "click" : true,
54932         /**
54933          * @event dblclick
54934          * The raw dblclick event for the entire grid.
54935          * @param {Roo.EventObject} e
54936          */
54937         "dblclick" : true,
54938         /**
54939          * @event contextmenu
54940          * The raw contextmenu event for the entire grid.
54941          * @param {Roo.EventObject} e
54942          */
54943         "contextmenu" : true,
54944         /**
54945          * @event mousedown
54946          * The raw mousedown event for the entire grid.
54947          * @param {Roo.EventObject} e
54948          */
54949         "mousedown" : true,
54950         /**
54951          * @event mouseup
54952          * The raw mouseup event for the entire grid.
54953          * @param {Roo.EventObject} e
54954          */
54955         "mouseup" : true,
54956         /**
54957          * @event mouseover
54958          * The raw mouseover event for the entire grid.
54959          * @param {Roo.EventObject} e
54960          */
54961         "mouseover" : true,
54962         /**
54963          * @event mouseout
54964          * The raw mouseout event for the entire grid.
54965          * @param {Roo.EventObject} e
54966          */
54967         "mouseout" : true,
54968         /**
54969          * @event keypress
54970          * The raw keypress event for the entire grid.
54971          * @param {Roo.EventObject} e
54972          */
54973         "keypress" : true,
54974         /**
54975          * @event keydown
54976          * The raw keydown event for the entire grid.
54977          * @param {Roo.EventObject} e
54978          */
54979         "keydown" : true,
54980
54981         // custom events
54982
54983         /**
54984          * @event cellclick
54985          * Fires when a cell is clicked
54986          * @param {Grid} this
54987          * @param {Number} rowIndex
54988          * @param {Number} columnIndex
54989          * @param {Roo.EventObject} e
54990          */
54991         "cellclick" : true,
54992         /**
54993          * @event celldblclick
54994          * Fires when a cell is double clicked
54995          * @param {Grid} this
54996          * @param {Number} rowIndex
54997          * @param {Number} columnIndex
54998          * @param {Roo.EventObject} e
54999          */
55000         "celldblclick" : true,
55001         /**
55002          * @event rowclick
55003          * Fires when a row is clicked
55004          * @param {Grid} this
55005          * @param {Number} rowIndex
55006          * @param {Roo.EventObject} e
55007          */
55008         "rowclick" : true,
55009         /**
55010          * @event rowdblclick
55011          * Fires when a row is double clicked
55012          * @param {Grid} this
55013          * @param {Number} rowIndex
55014          * @param {Roo.EventObject} e
55015          */
55016         "rowdblclick" : true,
55017         /**
55018          * @event headerclick
55019          * Fires when a header is clicked
55020          * @param {Grid} this
55021          * @param {Number} columnIndex
55022          * @param {Roo.EventObject} e
55023          */
55024         "headerclick" : true,
55025         /**
55026          * @event headerdblclick
55027          * Fires when a header cell is double clicked
55028          * @param {Grid} this
55029          * @param {Number} columnIndex
55030          * @param {Roo.EventObject} e
55031          */
55032         "headerdblclick" : true,
55033         /**
55034          * @event rowcontextmenu
55035          * Fires when a row is right clicked
55036          * @param {Grid} this
55037          * @param {Number} rowIndex
55038          * @param {Roo.EventObject} e
55039          */
55040         "rowcontextmenu" : true,
55041         /**
55042          * @event cellcontextmenu
55043          * Fires when a cell is right clicked
55044          * @param {Grid} this
55045          * @param {Number} rowIndex
55046          * @param {Number} cellIndex
55047          * @param {Roo.EventObject} e
55048          */
55049          "cellcontextmenu" : true,
55050         /**
55051          * @event headercontextmenu
55052          * Fires when a header is right clicked
55053          * @param {Grid} this
55054          * @param {Number} columnIndex
55055          * @param {Roo.EventObject} e
55056          */
55057         "headercontextmenu" : true,
55058         /**
55059          * @event bodyscroll
55060          * Fires when the body element is scrolled
55061          * @param {Number} scrollLeft
55062          * @param {Number} scrollTop
55063          */
55064         "bodyscroll" : true,
55065         /**
55066          * @event columnresize
55067          * Fires when the user resizes a column
55068          * @param {Number} columnIndex
55069          * @param {Number} newSize
55070          */
55071         "columnresize" : true,
55072         /**
55073          * @event columnmove
55074          * Fires when the user moves a column
55075          * @param {Number} oldIndex
55076          * @param {Number} newIndex
55077          */
55078         "columnmove" : true,
55079         /**
55080          * @event startdrag
55081          * Fires when row(s) start being dragged
55082          * @param {Grid} this
55083          * @param {Roo.GridDD} dd The drag drop object
55084          * @param {event} e The raw browser event
55085          */
55086         "startdrag" : true,
55087         /**
55088          * @event enddrag
55089          * Fires when a drag operation is complete
55090          * @param {Grid} this
55091          * @param {Roo.GridDD} dd The drag drop object
55092          * @param {event} e The raw browser event
55093          */
55094         "enddrag" : true,
55095         /**
55096          * @event dragdrop
55097          * Fires when dragged row(s) are dropped on a valid DD target
55098          * @param {Grid} this
55099          * @param {Roo.GridDD} dd The drag drop object
55100          * @param {String} targetId The target drag drop object
55101          * @param {event} e The raw browser event
55102          */
55103         "dragdrop" : true,
55104         /**
55105          * @event dragover
55106          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55107          * @param {Grid} this
55108          * @param {Roo.GridDD} dd The drag drop object
55109          * @param {String} targetId The target drag drop object
55110          * @param {event} e The raw browser event
55111          */
55112         "dragover" : true,
55113         /**
55114          * @event dragenter
55115          *  Fires when the dragged row(s) first cross another DD target while being dragged
55116          * @param {Grid} this
55117          * @param {Roo.GridDD} dd The drag drop object
55118          * @param {String} targetId The target drag drop object
55119          * @param {event} e The raw browser event
55120          */
55121         "dragenter" : true,
55122         /**
55123          * @event dragout
55124          * Fires when the dragged row(s) leave another DD target while being dragged
55125          * @param {Grid} this
55126          * @param {Roo.GridDD} dd The drag drop object
55127          * @param {String} targetId The target drag drop object
55128          * @param {event} e The raw browser event
55129          */
55130         "dragout" : true,
55131         /**
55132          * @event rowclass
55133          * Fires when a row is rendered, so you can change add a style to it.
55134          * @param {GridView} gridview   The grid view
55135          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55136          */
55137         'rowclass' : true,
55138
55139         /**
55140          * @event render
55141          * Fires when the grid is rendered
55142          * @param {Grid} grid
55143          */
55144         'render' : true
55145     });
55146
55147     Roo.grid.Grid.superclass.constructor.call(this);
55148 };
55149 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55150     
55151     /**
55152      * @cfg {String} ddGroup - drag drop group.
55153      */
55154
55155     /**
55156      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55157      */
55158     minColumnWidth : 25,
55159
55160     /**
55161      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55162      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55163      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55164      */
55165     autoSizeColumns : false,
55166
55167     /**
55168      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55169      */
55170     autoSizeHeaders : true,
55171
55172     /**
55173      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55174      */
55175     monitorWindowResize : true,
55176
55177     /**
55178      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55179      * rows measured to get a columns size. Default is 0 (all rows).
55180      */
55181     maxRowsToMeasure : 0,
55182
55183     /**
55184      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55185      */
55186     trackMouseOver : true,
55187
55188     /**
55189     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55190     */
55191     
55192     /**
55193     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55194     */
55195     enableDragDrop : false,
55196     
55197     /**
55198     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55199     */
55200     enableColumnMove : true,
55201     
55202     /**
55203     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55204     */
55205     enableColumnHide : true,
55206     
55207     /**
55208     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55209     */
55210     enableRowHeightSync : false,
55211     
55212     /**
55213     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55214     */
55215     stripeRows : true,
55216     
55217     /**
55218     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55219     */
55220     autoHeight : false,
55221
55222     /**
55223      * @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.
55224      */
55225     autoExpandColumn : false,
55226
55227     /**
55228     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55229     * Default is 50.
55230     */
55231     autoExpandMin : 50,
55232
55233     /**
55234     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55235     */
55236     autoExpandMax : 1000,
55237
55238     /**
55239     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55240     */
55241     view : null,
55242
55243     /**
55244     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55245     */
55246     loadMask : false,
55247     /**
55248     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55249     */
55250     dropTarget: false,
55251     
55252    
55253     
55254     // private
55255     rendered : false,
55256
55257     /**
55258     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55259     * of a fixed width. Default is false.
55260     */
55261     /**
55262     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55263     */
55264     /**
55265      * Called once after all setup has been completed and the grid is ready to be rendered.
55266      * @return {Roo.grid.Grid} this
55267      */
55268     render : function()
55269     {
55270         var c = this.container;
55271         // try to detect autoHeight/width mode
55272         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55273             this.autoHeight = true;
55274         }
55275         var view = this.getView();
55276         view.init(this);
55277
55278         c.on("click", this.onClick, this);
55279         c.on("dblclick", this.onDblClick, this);
55280         c.on("contextmenu", this.onContextMenu, this);
55281         c.on("keydown", this.onKeyDown, this);
55282         if (Roo.isTouch) {
55283             c.on("touchstart", this.onTouchStart, this);
55284         }
55285
55286         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55287
55288         this.getSelectionModel().init(this);
55289
55290         view.render();
55291
55292         if(this.loadMask){
55293             this.loadMask = new Roo.LoadMask(this.container,
55294                     Roo.apply({store:this.dataSource}, this.loadMask));
55295         }
55296         
55297         
55298         if (this.toolbar && this.toolbar.xtype) {
55299             this.toolbar.container = this.getView().getHeaderPanel(true);
55300             this.toolbar = new Roo.Toolbar(this.toolbar);
55301         }
55302         if (this.footer && this.footer.xtype) {
55303             this.footer.dataSource = this.getDataSource();
55304             this.footer.container = this.getView().getFooterPanel(true);
55305             this.footer = Roo.factory(this.footer, Roo);
55306         }
55307         if (this.dropTarget && this.dropTarget.xtype) {
55308             delete this.dropTarget.xtype;
55309             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55310         }
55311         
55312         
55313         this.rendered = true;
55314         this.fireEvent('render', this);
55315         return this;
55316     },
55317
55318         /**
55319          * Reconfigures the grid to use a different Store and Column Model.
55320          * The View will be bound to the new objects and refreshed.
55321          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55322          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55323          */
55324     reconfigure : function(dataSource, colModel){
55325         if(this.loadMask){
55326             this.loadMask.destroy();
55327             this.loadMask = new Roo.LoadMask(this.container,
55328                     Roo.apply({store:dataSource}, this.loadMask));
55329         }
55330         this.view.bind(dataSource, colModel);
55331         this.dataSource = dataSource;
55332         this.colModel = colModel;
55333         this.view.refresh(true);
55334     },
55335
55336     // private
55337     onKeyDown : function(e){
55338         this.fireEvent("keydown", e);
55339     },
55340
55341     /**
55342      * Destroy this grid.
55343      * @param {Boolean} removeEl True to remove the element
55344      */
55345     destroy : function(removeEl, keepListeners){
55346         if(this.loadMask){
55347             this.loadMask.destroy();
55348         }
55349         var c = this.container;
55350         c.removeAllListeners();
55351         this.view.destroy();
55352         this.colModel.purgeListeners();
55353         if(!keepListeners){
55354             this.purgeListeners();
55355         }
55356         c.update("");
55357         if(removeEl === true){
55358             c.remove();
55359         }
55360     },
55361
55362     // private
55363     processEvent : function(name, e){
55364         // does this fire select???
55365         //Roo.log('grid:processEvent '  + name);
55366         
55367         if (name != 'touchstart' ) {
55368             this.fireEvent(name, e);    
55369         }
55370         
55371         var t = e.getTarget();
55372         var v = this.view;
55373         var header = v.findHeaderIndex(t);
55374         if(header !== false){
55375             var ename = name == 'touchstart' ? 'click' : name;
55376              
55377             this.fireEvent("header" + ename, this, header, e);
55378         }else{
55379             var row = v.findRowIndex(t);
55380             var cell = v.findCellIndex(t);
55381             if (name == 'touchstart') {
55382                 // first touch is always a click.
55383                 // hopefull this happens after selection is updated.?
55384                 name = false;
55385                 
55386                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55387                     var cs = this.selModel.getSelectedCell();
55388                     if (row == cs[0] && cell == cs[1]){
55389                         name = 'dblclick';
55390                     }
55391                 }
55392                 if (typeof(this.selModel.getSelections) != 'undefined') {
55393                     var cs = this.selModel.getSelections();
55394                     var ds = this.dataSource;
55395                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55396                         name = 'dblclick';
55397                     }
55398                 }
55399                 if (!name) {
55400                     return;
55401                 }
55402             }
55403             
55404             
55405             if(row !== false){
55406                 this.fireEvent("row" + name, this, row, e);
55407                 if(cell !== false){
55408                     this.fireEvent("cell" + name, this, row, cell, e);
55409                 }
55410             }
55411         }
55412     },
55413
55414     // private
55415     onClick : function(e){
55416         this.processEvent("click", e);
55417     },
55418    // private
55419     onTouchStart : function(e){
55420         this.processEvent("touchstart", e);
55421     },
55422
55423     // private
55424     onContextMenu : function(e, t){
55425         this.processEvent("contextmenu", e);
55426     },
55427
55428     // private
55429     onDblClick : function(e){
55430         this.processEvent("dblclick", e);
55431     },
55432
55433     // private
55434     walkCells : function(row, col, step, fn, scope){
55435         var cm = this.colModel, clen = cm.getColumnCount();
55436         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55437         if(step < 0){
55438             if(col < 0){
55439                 row--;
55440                 first = false;
55441             }
55442             while(row >= 0){
55443                 if(!first){
55444                     col = clen-1;
55445                 }
55446                 first = false;
55447                 while(col >= 0){
55448                     if(fn.call(scope || this, row, col, cm) === true){
55449                         return [row, col];
55450                     }
55451                     col--;
55452                 }
55453                 row--;
55454             }
55455         } else {
55456             if(col >= clen){
55457                 row++;
55458                 first = false;
55459             }
55460             while(row < rlen){
55461                 if(!first){
55462                     col = 0;
55463                 }
55464                 first = false;
55465                 while(col < clen){
55466                     if(fn.call(scope || this, row, col, cm) === true){
55467                         return [row, col];
55468                     }
55469                     col++;
55470                 }
55471                 row++;
55472             }
55473         }
55474         return null;
55475     },
55476
55477     // private
55478     getSelections : function(){
55479         return this.selModel.getSelections();
55480     },
55481
55482     /**
55483      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55484      * but if manual update is required this method will initiate it.
55485      */
55486     autoSize : function(){
55487         if(this.rendered){
55488             this.view.layout();
55489             if(this.view.adjustForScroll){
55490                 this.view.adjustForScroll();
55491             }
55492         }
55493     },
55494
55495     /**
55496      * Returns the grid's underlying element.
55497      * @return {Element} The element
55498      */
55499     getGridEl : function(){
55500         return this.container;
55501     },
55502
55503     // private for compatibility, overridden by editor grid
55504     stopEditing : function(){},
55505
55506     /**
55507      * Returns the grid's SelectionModel.
55508      * @return {SelectionModel}
55509      */
55510     getSelectionModel : function(){
55511         if(!this.selModel){
55512             this.selModel = new Roo.grid.RowSelectionModel();
55513         }
55514         return this.selModel;
55515     },
55516
55517     /**
55518      * Returns the grid's DataSource.
55519      * @return {DataSource}
55520      */
55521     getDataSource : function(){
55522         return this.dataSource;
55523     },
55524
55525     /**
55526      * Returns the grid's ColumnModel.
55527      * @return {ColumnModel}
55528      */
55529     getColumnModel : function(){
55530         return this.colModel;
55531     },
55532
55533     /**
55534      * Returns the grid's GridView object.
55535      * @return {GridView}
55536      */
55537     getView : function(){
55538         if(!this.view){
55539             this.view = new Roo.grid.GridView(this.viewConfig);
55540         }
55541         return this.view;
55542     },
55543     /**
55544      * Called to get grid's drag proxy text, by default returns this.ddText.
55545      * @return {String}
55546      */
55547     getDragDropText : function(){
55548         var count = this.selModel.getCount();
55549         return String.format(this.ddText, count, count == 1 ? '' : 's');
55550     }
55551 });
55552 /**
55553  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55554  * %0 is replaced with the number of selected rows.
55555  * @type String
55556  */
55557 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
55558  * Based on:
55559  * Ext JS Library 1.1.1
55560  * Copyright(c) 2006-2007, Ext JS, LLC.
55561  *
55562  * Originally Released Under LGPL - original licence link has changed is not relivant.
55563  *
55564  * Fork - LGPL
55565  * <script type="text/javascript">
55566  */
55567  
55568 Roo.grid.AbstractGridView = function(){
55569         this.grid = null;
55570         
55571         this.events = {
55572             "beforerowremoved" : true,
55573             "beforerowsinserted" : true,
55574             "beforerefresh" : true,
55575             "rowremoved" : true,
55576             "rowsinserted" : true,
55577             "rowupdated" : true,
55578             "refresh" : true
55579         };
55580     Roo.grid.AbstractGridView.superclass.constructor.call(this);
55581 };
55582
55583 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
55584     rowClass : "x-grid-row",
55585     cellClass : "x-grid-cell",
55586     tdClass : "x-grid-td",
55587     hdClass : "x-grid-hd",
55588     splitClass : "x-grid-hd-split",
55589     
55590     init: function(grid){
55591         this.grid = grid;
55592                 var cid = this.grid.getGridEl().id;
55593         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
55594         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
55595         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
55596         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
55597         },
55598         
55599     getColumnRenderers : function(){
55600         var renderers = [];
55601         var cm = this.grid.colModel;
55602         var colCount = cm.getColumnCount();
55603         for(var i = 0; i < colCount; i++){
55604             renderers[i] = cm.getRenderer(i);
55605         }
55606         return renderers;
55607     },
55608     
55609     getColumnIds : function(){
55610         var ids = [];
55611         var cm = this.grid.colModel;
55612         var colCount = cm.getColumnCount();
55613         for(var i = 0; i < colCount; i++){
55614             ids[i] = cm.getColumnId(i);
55615         }
55616         return ids;
55617     },
55618     
55619     getDataIndexes : function(){
55620         if(!this.indexMap){
55621             this.indexMap = this.buildIndexMap();
55622         }
55623         return this.indexMap.colToData;
55624     },
55625     
55626     getColumnIndexByDataIndex : function(dataIndex){
55627         if(!this.indexMap){
55628             this.indexMap = this.buildIndexMap();
55629         }
55630         return this.indexMap.dataToCol[dataIndex];
55631     },
55632     
55633     /**
55634      * Set a css style for a column dynamically. 
55635      * @param {Number} colIndex The index of the column
55636      * @param {String} name The css property name
55637      * @param {String} value The css value
55638      */
55639     setCSSStyle : function(colIndex, name, value){
55640         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
55641         Roo.util.CSS.updateRule(selector, name, value);
55642     },
55643     
55644     generateRules : function(cm){
55645         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
55646         Roo.util.CSS.removeStyleSheet(rulesId);
55647         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55648             var cid = cm.getColumnId(i);
55649             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
55650                          this.tdSelector, cid, " {\n}\n",
55651                          this.hdSelector, cid, " {\n}\n",
55652                          this.splitSelector, cid, " {\n}\n");
55653         }
55654         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55655     }
55656 });/*
55657  * Based on:
55658  * Ext JS Library 1.1.1
55659  * Copyright(c) 2006-2007, Ext JS, LLC.
55660  *
55661  * Originally Released Under LGPL - original licence link has changed is not relivant.
55662  *
55663  * Fork - LGPL
55664  * <script type="text/javascript">
55665  */
55666
55667 // private
55668 // This is a support class used internally by the Grid components
55669 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
55670     this.grid = grid;
55671     this.view = grid.getView();
55672     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55673     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
55674     if(hd2){
55675         this.setHandleElId(Roo.id(hd));
55676         this.setOuterHandleElId(Roo.id(hd2));
55677     }
55678     this.scroll = false;
55679 };
55680 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
55681     maxDragWidth: 120,
55682     getDragData : function(e){
55683         var t = Roo.lib.Event.getTarget(e);
55684         var h = this.view.findHeaderCell(t);
55685         if(h){
55686             return {ddel: h.firstChild, header:h};
55687         }
55688         return false;
55689     },
55690
55691     onInitDrag : function(e){
55692         this.view.headersDisabled = true;
55693         var clone = this.dragData.ddel.cloneNode(true);
55694         clone.id = Roo.id();
55695         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
55696         this.proxy.update(clone);
55697         return true;
55698     },
55699
55700     afterValidDrop : function(){
55701         var v = this.view;
55702         setTimeout(function(){
55703             v.headersDisabled = false;
55704         }, 50);
55705     },
55706
55707     afterInvalidDrop : function(){
55708         var v = this.view;
55709         setTimeout(function(){
55710             v.headersDisabled = false;
55711         }, 50);
55712     }
55713 });
55714 /*
55715  * Based on:
55716  * Ext JS Library 1.1.1
55717  * Copyright(c) 2006-2007, Ext JS, LLC.
55718  *
55719  * Originally Released Under LGPL - original licence link has changed is not relivant.
55720  *
55721  * Fork - LGPL
55722  * <script type="text/javascript">
55723  */
55724 // private
55725 // This is a support class used internally by the Grid components
55726 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
55727     this.grid = grid;
55728     this.view = grid.getView();
55729     // split the proxies so they don't interfere with mouse events
55730     this.proxyTop = Roo.DomHelper.append(document.body, {
55731         cls:"col-move-top", html:"&#160;"
55732     }, true);
55733     this.proxyBottom = Roo.DomHelper.append(document.body, {
55734         cls:"col-move-bottom", html:"&#160;"
55735     }, true);
55736     this.proxyTop.hide = this.proxyBottom.hide = function(){
55737         this.setLeftTop(-100,-100);
55738         this.setStyle("visibility", "hidden");
55739     };
55740     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55741     // temporarily disabled
55742     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
55743     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
55744 };
55745 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
55746     proxyOffsets : [-4, -9],
55747     fly: Roo.Element.fly,
55748
55749     getTargetFromEvent : function(e){
55750         var t = Roo.lib.Event.getTarget(e);
55751         var cindex = this.view.findCellIndex(t);
55752         if(cindex !== false){
55753             return this.view.getHeaderCell(cindex);
55754         }
55755         return null;
55756     },
55757
55758     nextVisible : function(h){
55759         var v = this.view, cm = this.grid.colModel;
55760         h = h.nextSibling;
55761         while(h){
55762             if(!cm.isHidden(v.getCellIndex(h))){
55763                 return h;
55764             }
55765             h = h.nextSibling;
55766         }
55767         return null;
55768     },
55769
55770     prevVisible : function(h){
55771         var v = this.view, cm = this.grid.colModel;
55772         h = h.prevSibling;
55773         while(h){
55774             if(!cm.isHidden(v.getCellIndex(h))){
55775                 return h;
55776             }
55777             h = h.prevSibling;
55778         }
55779         return null;
55780     },
55781
55782     positionIndicator : function(h, n, e){
55783         var x = Roo.lib.Event.getPageX(e);
55784         var r = Roo.lib.Dom.getRegion(n.firstChild);
55785         var px, pt, py = r.top + this.proxyOffsets[1];
55786         if((r.right - x) <= (r.right-r.left)/2){
55787             px = r.right+this.view.borderWidth;
55788             pt = "after";
55789         }else{
55790             px = r.left;
55791             pt = "before";
55792         }
55793         var oldIndex = this.view.getCellIndex(h);
55794         var newIndex = this.view.getCellIndex(n);
55795
55796         if(this.grid.colModel.isFixed(newIndex)){
55797             return false;
55798         }
55799
55800         var locked = this.grid.colModel.isLocked(newIndex);
55801
55802         if(pt == "after"){
55803             newIndex++;
55804         }
55805         if(oldIndex < newIndex){
55806             newIndex--;
55807         }
55808         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
55809             return false;
55810         }
55811         px +=  this.proxyOffsets[0];
55812         this.proxyTop.setLeftTop(px, py);
55813         this.proxyTop.show();
55814         if(!this.bottomOffset){
55815             this.bottomOffset = this.view.mainHd.getHeight();
55816         }
55817         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
55818         this.proxyBottom.show();
55819         return pt;
55820     },
55821
55822     onNodeEnter : function(n, dd, e, data){
55823         if(data.header != n){
55824             this.positionIndicator(data.header, n, e);
55825         }
55826     },
55827
55828     onNodeOver : function(n, dd, e, data){
55829         var result = false;
55830         if(data.header != n){
55831             result = this.positionIndicator(data.header, n, e);
55832         }
55833         if(!result){
55834             this.proxyTop.hide();
55835             this.proxyBottom.hide();
55836         }
55837         return result ? this.dropAllowed : this.dropNotAllowed;
55838     },
55839
55840     onNodeOut : function(n, dd, e, data){
55841         this.proxyTop.hide();
55842         this.proxyBottom.hide();
55843     },
55844
55845     onNodeDrop : function(n, dd, e, data){
55846         var h = data.header;
55847         if(h != n){
55848             var cm = this.grid.colModel;
55849             var x = Roo.lib.Event.getPageX(e);
55850             var r = Roo.lib.Dom.getRegion(n.firstChild);
55851             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
55852             var oldIndex = this.view.getCellIndex(h);
55853             var newIndex = this.view.getCellIndex(n);
55854             var locked = cm.isLocked(newIndex);
55855             if(pt == "after"){
55856                 newIndex++;
55857             }
55858             if(oldIndex < newIndex){
55859                 newIndex--;
55860             }
55861             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
55862                 return false;
55863             }
55864             cm.setLocked(oldIndex, locked, true);
55865             cm.moveColumn(oldIndex, newIndex);
55866             this.grid.fireEvent("columnmove", oldIndex, newIndex);
55867             return true;
55868         }
55869         return false;
55870     }
55871 });
55872 /*
55873  * Based on:
55874  * Ext JS Library 1.1.1
55875  * Copyright(c) 2006-2007, Ext JS, LLC.
55876  *
55877  * Originally Released Under LGPL - original licence link has changed is not relivant.
55878  *
55879  * Fork - LGPL
55880  * <script type="text/javascript">
55881  */
55882   
55883 /**
55884  * @class Roo.grid.GridView
55885  * @extends Roo.util.Observable
55886  *
55887  * @constructor
55888  * @param {Object} config
55889  */
55890 Roo.grid.GridView = function(config){
55891     Roo.grid.GridView.superclass.constructor.call(this);
55892     this.el = null;
55893
55894     Roo.apply(this, config);
55895 };
55896
55897 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55898
55899     unselectable :  'unselectable="on"',
55900     unselectableCls :  'x-unselectable',
55901     
55902     
55903     rowClass : "x-grid-row",
55904
55905     cellClass : "x-grid-col",
55906
55907     tdClass : "x-grid-td",
55908
55909     hdClass : "x-grid-hd",
55910
55911     splitClass : "x-grid-split",
55912
55913     sortClasses : ["sort-asc", "sort-desc"],
55914
55915     enableMoveAnim : false,
55916
55917     hlColor: "C3DAF9",
55918
55919     dh : Roo.DomHelper,
55920
55921     fly : Roo.Element.fly,
55922
55923     css : Roo.util.CSS,
55924
55925     borderWidth: 1,
55926
55927     splitOffset: 3,
55928
55929     scrollIncrement : 22,
55930
55931     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55932
55933     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55934
55935     bind : function(ds, cm){
55936         if(this.ds){
55937             this.ds.un("load", this.onLoad, this);
55938             this.ds.un("datachanged", this.onDataChange, this);
55939             this.ds.un("add", this.onAdd, this);
55940             this.ds.un("remove", this.onRemove, this);
55941             this.ds.un("update", this.onUpdate, this);
55942             this.ds.un("clear", this.onClear, this);
55943         }
55944         if(ds){
55945             ds.on("load", this.onLoad, this);
55946             ds.on("datachanged", this.onDataChange, this);
55947             ds.on("add", this.onAdd, this);
55948             ds.on("remove", this.onRemove, this);
55949             ds.on("update", this.onUpdate, this);
55950             ds.on("clear", this.onClear, this);
55951         }
55952         this.ds = ds;
55953
55954         if(this.cm){
55955             this.cm.un("widthchange", this.onColWidthChange, this);
55956             this.cm.un("headerchange", this.onHeaderChange, this);
55957             this.cm.un("hiddenchange", this.onHiddenChange, this);
55958             this.cm.un("columnmoved", this.onColumnMove, this);
55959             this.cm.un("columnlockchange", this.onColumnLock, this);
55960         }
55961         if(cm){
55962             this.generateRules(cm);
55963             cm.on("widthchange", this.onColWidthChange, this);
55964             cm.on("headerchange", this.onHeaderChange, this);
55965             cm.on("hiddenchange", this.onHiddenChange, this);
55966             cm.on("columnmoved", this.onColumnMove, this);
55967             cm.on("columnlockchange", this.onColumnLock, this);
55968         }
55969         this.cm = cm;
55970     },
55971
55972     init: function(grid){
55973         Roo.grid.GridView.superclass.init.call(this, grid);
55974
55975         this.bind(grid.dataSource, grid.colModel);
55976
55977         grid.on("headerclick", this.handleHeaderClick, this);
55978
55979         if(grid.trackMouseOver){
55980             grid.on("mouseover", this.onRowOver, this);
55981             grid.on("mouseout", this.onRowOut, this);
55982         }
55983         grid.cancelTextSelection = function(){};
55984         this.gridId = grid.id;
55985
55986         var tpls = this.templates || {};
55987
55988         if(!tpls.master){
55989             tpls.master = new Roo.Template(
55990                '<div class="x-grid" hidefocus="true">',
55991                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55992                   '<div class="x-grid-topbar"></div>',
55993                   '<div class="x-grid-scroller"><div></div></div>',
55994                   '<div class="x-grid-locked">',
55995                       '<div class="x-grid-header">{lockedHeader}</div>',
55996                       '<div class="x-grid-body">{lockedBody}</div>',
55997                   "</div>",
55998                   '<div class="x-grid-viewport">',
55999                       '<div class="x-grid-header">{header}</div>',
56000                       '<div class="x-grid-body">{body}</div>',
56001                   "</div>",
56002                   '<div class="x-grid-bottombar"></div>',
56003                  
56004                   '<div class="x-grid-resize-proxy">&#160;</div>',
56005                "</div>"
56006             );
56007             tpls.master.disableformats = true;
56008         }
56009
56010         if(!tpls.header){
56011             tpls.header = new Roo.Template(
56012                '<table border="0" cellspacing="0" cellpadding="0">',
56013                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56014                "</table>{splits}"
56015             );
56016             tpls.header.disableformats = true;
56017         }
56018         tpls.header.compile();
56019
56020         if(!tpls.hcell){
56021             tpls.hcell = new Roo.Template(
56022                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56023                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56024                 "</div></td>"
56025              );
56026              tpls.hcell.disableFormats = true;
56027         }
56028         tpls.hcell.compile();
56029
56030         if(!tpls.hsplit){
56031             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56032                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56033             tpls.hsplit.disableFormats = true;
56034         }
56035         tpls.hsplit.compile();
56036
56037         if(!tpls.body){
56038             tpls.body = new Roo.Template(
56039                '<table border="0" cellspacing="0" cellpadding="0">',
56040                "<tbody>{rows}</tbody>",
56041                "</table>"
56042             );
56043             tpls.body.disableFormats = true;
56044         }
56045         tpls.body.compile();
56046
56047         if(!tpls.row){
56048             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56049             tpls.row.disableFormats = true;
56050         }
56051         tpls.row.compile();
56052
56053         if(!tpls.cell){
56054             tpls.cell = new Roo.Template(
56055                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56056                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56057                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56058                 "</td>"
56059             );
56060             tpls.cell.disableFormats = true;
56061         }
56062         tpls.cell.compile();
56063
56064         this.templates = tpls;
56065     },
56066
56067     // remap these for backwards compat
56068     onColWidthChange : function(){
56069         this.updateColumns.apply(this, arguments);
56070     },
56071     onHeaderChange : function(){
56072         this.updateHeaders.apply(this, arguments);
56073     }, 
56074     onHiddenChange : function(){
56075         this.handleHiddenChange.apply(this, arguments);
56076     },
56077     onColumnMove : function(){
56078         this.handleColumnMove.apply(this, arguments);
56079     },
56080     onColumnLock : function(){
56081         this.handleLockChange.apply(this, arguments);
56082     },
56083
56084     onDataChange : function(){
56085         this.refresh();
56086         this.updateHeaderSortState();
56087     },
56088
56089     onClear : function(){
56090         this.refresh();
56091     },
56092
56093     onUpdate : function(ds, record){
56094         this.refreshRow(record);
56095     },
56096
56097     refreshRow : function(record){
56098         var ds = this.ds, index;
56099         if(typeof record == 'number'){
56100             index = record;
56101             record = ds.getAt(index);
56102         }else{
56103             index = ds.indexOf(record);
56104         }
56105         this.insertRows(ds, index, index, true);
56106         this.onRemove(ds, record, index+1, true);
56107         this.syncRowHeights(index, index);
56108         this.layout();
56109         this.fireEvent("rowupdated", this, index, record);
56110     },
56111
56112     onAdd : function(ds, records, index){
56113         this.insertRows(ds, index, index + (records.length-1));
56114     },
56115
56116     onRemove : function(ds, record, index, isUpdate){
56117         if(isUpdate !== true){
56118             this.fireEvent("beforerowremoved", this, index, record);
56119         }
56120         var bt = this.getBodyTable(), lt = this.getLockedTable();
56121         if(bt.rows[index]){
56122             bt.firstChild.removeChild(bt.rows[index]);
56123         }
56124         if(lt.rows[index]){
56125             lt.firstChild.removeChild(lt.rows[index]);
56126         }
56127         if(isUpdate !== true){
56128             this.stripeRows(index);
56129             this.syncRowHeights(index, index);
56130             this.layout();
56131             this.fireEvent("rowremoved", this, index, record);
56132         }
56133     },
56134
56135     onLoad : function(){
56136         this.scrollToTop();
56137     },
56138
56139     /**
56140      * Scrolls the grid to the top
56141      */
56142     scrollToTop : function(){
56143         if(this.scroller){
56144             this.scroller.dom.scrollTop = 0;
56145             this.syncScroll();
56146         }
56147     },
56148
56149     /**
56150      * Gets a panel in the header of the grid that can be used for toolbars etc.
56151      * After modifying the contents of this panel a call to grid.autoSize() may be
56152      * required to register any changes in size.
56153      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56154      * @return Roo.Element
56155      */
56156     getHeaderPanel : function(doShow){
56157         if(doShow){
56158             this.headerPanel.show();
56159         }
56160         return this.headerPanel;
56161     },
56162
56163     /**
56164      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56165      * After modifying the contents of this panel a call to grid.autoSize() may be
56166      * required to register any changes in size.
56167      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56168      * @return Roo.Element
56169      */
56170     getFooterPanel : function(doShow){
56171         if(doShow){
56172             this.footerPanel.show();
56173         }
56174         return this.footerPanel;
56175     },
56176
56177     initElements : function(){
56178         var E = Roo.Element;
56179         var el = this.grid.getGridEl().dom.firstChild;
56180         var cs = el.childNodes;
56181
56182         this.el = new E(el);
56183         
56184          this.focusEl = new E(el.firstChild);
56185         this.focusEl.swallowEvent("click", true);
56186         
56187         this.headerPanel = new E(cs[1]);
56188         this.headerPanel.enableDisplayMode("block");
56189
56190         this.scroller = new E(cs[2]);
56191         this.scrollSizer = new E(this.scroller.dom.firstChild);
56192
56193         this.lockedWrap = new E(cs[3]);
56194         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56195         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56196
56197         this.mainWrap = new E(cs[4]);
56198         this.mainHd = new E(this.mainWrap.dom.firstChild);
56199         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56200
56201         this.footerPanel = new E(cs[5]);
56202         this.footerPanel.enableDisplayMode("block");
56203
56204         this.resizeProxy = new E(cs[6]);
56205
56206         this.headerSelector = String.format(
56207            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56208            this.lockedHd.id, this.mainHd.id
56209         );
56210
56211         this.splitterSelector = String.format(
56212            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56213            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56214         );
56215     },
56216     idToCssName : function(s)
56217     {
56218         return s.replace(/[^a-z0-9]+/ig, '-');
56219     },
56220
56221     getHeaderCell : function(index){
56222         return Roo.DomQuery.select(this.headerSelector)[index];
56223     },
56224
56225     getHeaderCellMeasure : function(index){
56226         return this.getHeaderCell(index).firstChild;
56227     },
56228
56229     getHeaderCellText : function(index){
56230         return this.getHeaderCell(index).firstChild.firstChild;
56231     },
56232
56233     getLockedTable : function(){
56234         return this.lockedBody.dom.firstChild;
56235     },
56236
56237     getBodyTable : function(){
56238         return this.mainBody.dom.firstChild;
56239     },
56240
56241     getLockedRow : function(index){
56242         return this.getLockedTable().rows[index];
56243     },
56244
56245     getRow : function(index){
56246         return this.getBodyTable().rows[index];
56247     },
56248
56249     getRowComposite : function(index){
56250         if(!this.rowEl){
56251             this.rowEl = new Roo.CompositeElementLite();
56252         }
56253         var els = [], lrow, mrow;
56254         if(lrow = this.getLockedRow(index)){
56255             els.push(lrow);
56256         }
56257         if(mrow = this.getRow(index)){
56258             els.push(mrow);
56259         }
56260         this.rowEl.elements = els;
56261         return this.rowEl;
56262     },
56263     /**
56264      * Gets the 'td' of the cell
56265      * 
56266      * @param {Integer} rowIndex row to select
56267      * @param {Integer} colIndex column to select
56268      * 
56269      * @return {Object} 
56270      */
56271     getCell : function(rowIndex, colIndex){
56272         var locked = this.cm.getLockedCount();
56273         var source;
56274         if(colIndex < locked){
56275             source = this.lockedBody.dom.firstChild;
56276         }else{
56277             source = this.mainBody.dom.firstChild;
56278             colIndex -= locked;
56279         }
56280         return source.rows[rowIndex].childNodes[colIndex];
56281     },
56282
56283     getCellText : function(rowIndex, colIndex){
56284         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56285     },
56286
56287     getCellBox : function(cell){
56288         var b = this.fly(cell).getBox();
56289         if(Roo.isOpera){ // opera fails to report the Y
56290             b.y = cell.offsetTop + this.mainBody.getY();
56291         }
56292         return b;
56293     },
56294
56295     getCellIndex : function(cell){
56296         var id = String(cell.className).match(this.cellRE);
56297         if(id){
56298             return parseInt(id[1], 10);
56299         }
56300         return 0;
56301     },
56302
56303     findHeaderIndex : function(n){
56304         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56305         return r ? this.getCellIndex(r) : false;
56306     },
56307
56308     findHeaderCell : function(n){
56309         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56310         return r ? r : false;
56311     },
56312
56313     findRowIndex : function(n){
56314         if(!n){
56315             return false;
56316         }
56317         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56318         return r ? r.rowIndex : false;
56319     },
56320
56321     findCellIndex : function(node){
56322         var stop = this.el.dom;
56323         while(node && node != stop){
56324             if(this.findRE.test(node.className)){
56325                 return this.getCellIndex(node);
56326             }
56327             node = node.parentNode;
56328         }
56329         return false;
56330     },
56331
56332     getColumnId : function(index){
56333         return this.cm.getColumnId(index);
56334     },
56335
56336     getSplitters : function()
56337     {
56338         if(this.splitterSelector){
56339            return Roo.DomQuery.select(this.splitterSelector);
56340         }else{
56341             return null;
56342       }
56343     },
56344
56345     getSplitter : function(index){
56346         return this.getSplitters()[index];
56347     },
56348
56349     onRowOver : function(e, t){
56350         var row;
56351         if((row = this.findRowIndex(t)) !== false){
56352             this.getRowComposite(row).addClass("x-grid-row-over");
56353         }
56354     },
56355
56356     onRowOut : function(e, t){
56357         var row;
56358         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56359             this.getRowComposite(row).removeClass("x-grid-row-over");
56360         }
56361     },
56362
56363     renderHeaders : function(){
56364         var cm = this.cm;
56365         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56366         var cb = [], lb = [], sb = [], lsb = [], p = {};
56367         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56368             p.cellId = "x-grid-hd-0-" + i;
56369             p.splitId = "x-grid-csplit-0-" + i;
56370             p.id = cm.getColumnId(i);
56371             p.value = cm.getColumnHeader(i) || "";
56372             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56373             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56374             if(!cm.isLocked(i)){
56375                 cb[cb.length] = ct.apply(p);
56376                 sb[sb.length] = st.apply(p);
56377             }else{
56378                 lb[lb.length] = ct.apply(p);
56379                 lsb[lsb.length] = st.apply(p);
56380             }
56381         }
56382         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56383                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56384     },
56385
56386     updateHeaders : function(){
56387         var html = this.renderHeaders();
56388         this.lockedHd.update(html[0]);
56389         this.mainHd.update(html[1]);
56390     },
56391
56392     /**
56393      * Focuses the specified row.
56394      * @param {Number} row The row index
56395      */
56396     focusRow : function(row)
56397     {
56398         //Roo.log('GridView.focusRow');
56399         var x = this.scroller.dom.scrollLeft;
56400         this.focusCell(row, 0, false);
56401         this.scroller.dom.scrollLeft = x;
56402     },
56403
56404     /**
56405      * Focuses the specified cell.
56406      * @param {Number} row The row index
56407      * @param {Number} col The column index
56408      * @param {Boolean} hscroll false to disable horizontal scrolling
56409      */
56410     focusCell : function(row, col, hscroll)
56411     {
56412         //Roo.log('GridView.focusCell');
56413         var el = this.ensureVisible(row, col, hscroll);
56414         this.focusEl.alignTo(el, "tl-tl");
56415         if(Roo.isGecko){
56416             this.focusEl.focus();
56417         }else{
56418             this.focusEl.focus.defer(1, this.focusEl);
56419         }
56420     },
56421
56422     /**
56423      * Scrolls the specified cell into view
56424      * @param {Number} row The row index
56425      * @param {Number} col The column index
56426      * @param {Boolean} hscroll false to disable horizontal scrolling
56427      */
56428     ensureVisible : function(row, col, hscroll)
56429     {
56430         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56431         //return null; //disable for testing.
56432         if(typeof row != "number"){
56433             row = row.rowIndex;
56434         }
56435         if(row < 0 && row >= this.ds.getCount()){
56436             return  null;
56437         }
56438         col = (col !== undefined ? col : 0);
56439         var cm = this.grid.colModel;
56440         while(cm.isHidden(col)){
56441             col++;
56442         }
56443
56444         var el = this.getCell(row, col);
56445         if(!el){
56446             return null;
56447         }
56448         var c = this.scroller.dom;
56449
56450         var ctop = parseInt(el.offsetTop, 10);
56451         var cleft = parseInt(el.offsetLeft, 10);
56452         var cbot = ctop + el.offsetHeight;
56453         var cright = cleft + el.offsetWidth;
56454         
56455         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56456         var stop = parseInt(c.scrollTop, 10);
56457         var sleft = parseInt(c.scrollLeft, 10);
56458         var sbot = stop + ch;
56459         var sright = sleft + c.clientWidth;
56460         /*
56461         Roo.log('GridView.ensureVisible:' +
56462                 ' ctop:' + ctop +
56463                 ' c.clientHeight:' + c.clientHeight +
56464                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56465                 ' stop:' + stop +
56466                 ' cbot:' + cbot +
56467                 ' sbot:' + sbot +
56468                 ' ch:' + ch  
56469                 );
56470         */
56471         if(ctop < stop){
56472              c.scrollTop = ctop;
56473             //Roo.log("set scrolltop to ctop DISABLE?");
56474         }else if(cbot > sbot){
56475             //Roo.log("set scrolltop to cbot-ch");
56476             c.scrollTop = cbot-ch;
56477         }
56478         
56479         if(hscroll !== false){
56480             if(cleft < sleft){
56481                 c.scrollLeft = cleft;
56482             }else if(cright > sright){
56483                 c.scrollLeft = cright-c.clientWidth;
56484             }
56485         }
56486          
56487         return el;
56488     },
56489
56490     updateColumns : function(){
56491         this.grid.stopEditing();
56492         var cm = this.grid.colModel, colIds = this.getColumnIds();
56493         //var totalWidth = cm.getTotalWidth();
56494         var pos = 0;
56495         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56496             //if(cm.isHidden(i)) continue;
56497             var w = cm.getColumnWidth(i);
56498             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56499             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56500         }
56501         this.updateSplitters();
56502     },
56503
56504     generateRules : function(cm){
56505         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56506         Roo.util.CSS.removeStyleSheet(rulesId);
56507         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56508             var cid = cm.getColumnId(i);
56509             var align = '';
56510             if(cm.config[i].align){
56511                 align = 'text-align:'+cm.config[i].align+';';
56512             }
56513             var hidden = '';
56514             if(cm.isHidden(i)){
56515                 hidden = 'display:none;';
56516             }
56517             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56518             ruleBuf.push(
56519                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56520                     this.hdSelector, cid, " {\n", align, width, "}\n",
56521                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56522                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56523         }
56524         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56525     },
56526
56527     updateSplitters : function(){
56528         var cm = this.cm, s = this.getSplitters();
56529         if(s){ // splitters not created yet
56530             var pos = 0, locked = true;
56531             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56532                 if(cm.isHidden(i)) {
56533                     continue;
56534                 }
56535                 var w = cm.getColumnWidth(i); // make sure it's a number
56536                 if(!cm.isLocked(i) && locked){
56537                     pos = 0;
56538                     locked = false;
56539                 }
56540                 pos += w;
56541                 s[i].style.left = (pos-this.splitOffset) + "px";
56542             }
56543         }
56544     },
56545
56546     handleHiddenChange : function(colModel, colIndex, hidden){
56547         if(hidden){
56548             this.hideColumn(colIndex);
56549         }else{
56550             this.unhideColumn(colIndex);
56551         }
56552     },
56553
56554     hideColumn : function(colIndex){
56555         var cid = this.getColumnId(colIndex);
56556         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
56557         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
56558         if(Roo.isSafari){
56559             this.updateHeaders();
56560         }
56561         this.updateSplitters();
56562         this.layout();
56563     },
56564
56565     unhideColumn : function(colIndex){
56566         var cid = this.getColumnId(colIndex);
56567         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
56568         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
56569
56570         if(Roo.isSafari){
56571             this.updateHeaders();
56572         }
56573         this.updateSplitters();
56574         this.layout();
56575     },
56576
56577     insertRows : function(dm, firstRow, lastRow, isUpdate){
56578         if(firstRow == 0 && lastRow == dm.getCount()-1){
56579             this.refresh();
56580         }else{
56581             if(!isUpdate){
56582                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
56583             }
56584             var s = this.getScrollState();
56585             var markup = this.renderRows(firstRow, lastRow);
56586             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
56587             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
56588             this.restoreScroll(s);
56589             if(!isUpdate){
56590                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
56591                 this.syncRowHeights(firstRow, lastRow);
56592                 this.stripeRows(firstRow);
56593                 this.layout();
56594             }
56595         }
56596     },
56597
56598     bufferRows : function(markup, target, index){
56599         var before = null, trows = target.rows, tbody = target.tBodies[0];
56600         if(index < trows.length){
56601             before = trows[index];
56602         }
56603         var b = document.createElement("div");
56604         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
56605         var rows = b.firstChild.rows;
56606         for(var i = 0, len = rows.length; i < len; i++){
56607             if(before){
56608                 tbody.insertBefore(rows[0], before);
56609             }else{
56610                 tbody.appendChild(rows[0]);
56611             }
56612         }
56613         b.innerHTML = "";
56614         b = null;
56615     },
56616
56617     deleteRows : function(dm, firstRow, lastRow){
56618         if(dm.getRowCount()<1){
56619             this.fireEvent("beforerefresh", this);
56620             this.mainBody.update("");
56621             this.lockedBody.update("");
56622             this.fireEvent("refresh", this);
56623         }else{
56624             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
56625             var bt = this.getBodyTable();
56626             var tbody = bt.firstChild;
56627             var rows = bt.rows;
56628             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
56629                 tbody.removeChild(rows[firstRow]);
56630             }
56631             this.stripeRows(firstRow);
56632             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
56633         }
56634     },
56635
56636     updateRows : function(dataSource, firstRow, lastRow){
56637         var s = this.getScrollState();
56638         this.refresh();
56639         this.restoreScroll(s);
56640     },
56641
56642     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
56643         if(!noRefresh){
56644            this.refresh();
56645         }
56646         this.updateHeaderSortState();
56647     },
56648
56649     getScrollState : function(){
56650         
56651         var sb = this.scroller.dom;
56652         return {left: sb.scrollLeft, top: sb.scrollTop};
56653     },
56654
56655     stripeRows : function(startRow){
56656         if(!this.grid.stripeRows || this.ds.getCount() < 1){
56657             return;
56658         }
56659         startRow = startRow || 0;
56660         var rows = this.getBodyTable().rows;
56661         var lrows = this.getLockedTable().rows;
56662         var cls = ' x-grid-row-alt ';
56663         for(var i = startRow, len = rows.length; i < len; i++){
56664             var row = rows[i], lrow = lrows[i];
56665             var isAlt = ((i+1) % 2 == 0);
56666             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
56667             if(isAlt == hasAlt){
56668                 continue;
56669             }
56670             if(isAlt){
56671                 row.className += " x-grid-row-alt";
56672             }else{
56673                 row.className = row.className.replace("x-grid-row-alt", "");
56674             }
56675             if(lrow){
56676                 lrow.className = row.className;
56677             }
56678         }
56679     },
56680
56681     restoreScroll : function(state){
56682         //Roo.log('GridView.restoreScroll');
56683         var sb = this.scroller.dom;
56684         sb.scrollLeft = state.left;
56685         sb.scrollTop = state.top;
56686         this.syncScroll();
56687     },
56688
56689     syncScroll : function(){
56690         //Roo.log('GridView.syncScroll');
56691         var sb = this.scroller.dom;
56692         var sh = this.mainHd.dom;
56693         var bs = this.mainBody.dom;
56694         var lv = this.lockedBody.dom;
56695         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
56696         lv.scrollTop = bs.scrollTop = sb.scrollTop;
56697     },
56698
56699     handleScroll : function(e){
56700         this.syncScroll();
56701         var sb = this.scroller.dom;
56702         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
56703         e.stopEvent();
56704     },
56705
56706     handleWheel : function(e){
56707         var d = e.getWheelDelta();
56708         this.scroller.dom.scrollTop -= d*22;
56709         // set this here to prevent jumpy scrolling on large tables
56710         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
56711         e.stopEvent();
56712     },
56713
56714     renderRows : function(startRow, endRow){
56715         // pull in all the crap needed to render rows
56716         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
56717         var colCount = cm.getColumnCount();
56718
56719         if(ds.getCount() < 1){
56720             return ["", ""];
56721         }
56722
56723         // build a map for all the columns
56724         var cs = [];
56725         for(var i = 0; i < colCount; i++){
56726             var name = cm.getDataIndex(i);
56727             cs[i] = {
56728                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
56729                 renderer : cm.getRenderer(i),
56730                 id : cm.getColumnId(i),
56731                 locked : cm.isLocked(i),
56732                 has_editor : cm.isCellEditable(i)
56733             };
56734         }
56735
56736         startRow = startRow || 0;
56737         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
56738
56739         // records to render
56740         var rs = ds.getRange(startRow, endRow);
56741
56742         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
56743     },
56744
56745     // As much as I hate to duplicate code, this was branched because FireFox really hates
56746     // [].join("") on strings. The performance difference was substantial enough to
56747     // branch this function
56748     doRender : Roo.isGecko ?
56749             function(cs, rs, ds, startRow, colCount, stripe){
56750                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56751                 // buffers
56752                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56753                 
56754                 var hasListener = this.grid.hasListener('rowclass');
56755                 var rowcfg = {};
56756                 for(var j = 0, len = rs.length; j < len; j++){
56757                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
56758                     for(var i = 0; i < colCount; i++){
56759                         c = cs[i];
56760                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56761                         p.id = c.id;
56762                         p.css = p.attr = "";
56763                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56764                         if(p.value == undefined || p.value === "") {
56765                             p.value = "&#160;";
56766                         }
56767                         if(c.has_editor){
56768                             p.css += ' x-grid-editable-cell';
56769                         }
56770                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
56771                             p.css +=  ' x-grid-dirty-cell';
56772                         }
56773                         var markup = ct.apply(p);
56774                         if(!c.locked){
56775                             cb+= markup;
56776                         }else{
56777                             lcb+= markup;
56778                         }
56779                     }
56780                     var alt = [];
56781                     if(stripe && ((rowIndex+1) % 2 == 0)){
56782                         alt.push("x-grid-row-alt")
56783                     }
56784                     if(r.dirty){
56785                         alt.push(  " x-grid-dirty-row");
56786                     }
56787                     rp.cells = lcb;
56788                     if(this.getRowClass){
56789                         alt.push(this.getRowClass(r, rowIndex));
56790                     }
56791                     if (hasListener) {
56792                         rowcfg = {
56793                              
56794                             record: r,
56795                             rowIndex : rowIndex,
56796                             rowClass : ''
56797                         };
56798                         this.grid.fireEvent('rowclass', this, rowcfg);
56799                         alt.push(rowcfg.rowClass);
56800                     }
56801                     rp.alt = alt.join(" ");
56802                     lbuf+= rt.apply(rp);
56803                     rp.cells = cb;
56804                     buf+=  rt.apply(rp);
56805                 }
56806                 return [lbuf, buf];
56807             } :
56808             function(cs, rs, ds, startRow, colCount, stripe){
56809                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56810                 // buffers
56811                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56812                 var hasListener = this.grid.hasListener('rowclass');
56813  
56814                 var rowcfg = {};
56815                 for(var j = 0, len = rs.length; j < len; j++){
56816                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
56817                     for(var i = 0; i < colCount; i++){
56818                         c = cs[i];
56819                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56820                         p.id = c.id;
56821                         p.css = p.attr = "";
56822                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56823                         if(p.value == undefined || p.value === "") {
56824                             p.value = "&#160;";
56825                         }
56826                         //Roo.log(c);
56827                          if(c.has_editor){
56828                             p.css += ' x-grid-editable-cell';
56829                         }
56830                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
56831                             p.css += ' x-grid-dirty-cell' 
56832                         }
56833                         
56834                         var markup = ct.apply(p);
56835                         if(!c.locked){
56836                             cb[cb.length] = markup;
56837                         }else{
56838                             lcb[lcb.length] = markup;
56839                         }
56840                     }
56841                     var alt = [];
56842                     if(stripe && ((rowIndex+1) % 2 == 0)){
56843                         alt.push( "x-grid-row-alt");
56844                     }
56845                     if(r.dirty){
56846                         alt.push(" x-grid-dirty-row");
56847                     }
56848                     rp.cells = lcb;
56849                     if(this.getRowClass){
56850                         alt.push( this.getRowClass(r, rowIndex));
56851                     }
56852                     if (hasListener) {
56853                         rowcfg = {
56854                              
56855                             record: r,
56856                             rowIndex : rowIndex,
56857                             rowClass : ''
56858                         };
56859                         this.grid.fireEvent('rowclass', this, rowcfg);
56860                         alt.push(rowcfg.rowClass);
56861                     }
56862                     
56863                     rp.alt = alt.join(" ");
56864                     rp.cells = lcb.join("");
56865                     lbuf[lbuf.length] = rt.apply(rp);
56866                     rp.cells = cb.join("");
56867                     buf[buf.length] =  rt.apply(rp);
56868                 }
56869                 return [lbuf.join(""), buf.join("")];
56870             },
56871
56872     renderBody : function(){
56873         var markup = this.renderRows();
56874         var bt = this.templates.body;
56875         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56876     },
56877
56878     /**
56879      * Refreshes the grid
56880      * @param {Boolean} headersToo
56881      */
56882     refresh : function(headersToo){
56883         this.fireEvent("beforerefresh", this);
56884         this.grid.stopEditing();
56885         var result = this.renderBody();
56886         this.lockedBody.update(result[0]);
56887         this.mainBody.update(result[1]);
56888         if(headersToo === true){
56889             this.updateHeaders();
56890             this.updateColumns();
56891             this.updateSplitters();
56892             this.updateHeaderSortState();
56893         }
56894         this.syncRowHeights();
56895         this.layout();
56896         this.fireEvent("refresh", this);
56897     },
56898
56899     handleColumnMove : function(cm, oldIndex, newIndex){
56900         this.indexMap = null;
56901         var s = this.getScrollState();
56902         this.refresh(true);
56903         this.restoreScroll(s);
56904         this.afterMove(newIndex);
56905     },
56906
56907     afterMove : function(colIndex){
56908         if(this.enableMoveAnim && Roo.enableFx){
56909             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56910         }
56911         // if multisort - fix sortOrder, and reload..
56912         if (this.grid.dataSource.multiSort) {
56913             // the we can call sort again..
56914             var dm = this.grid.dataSource;
56915             var cm = this.grid.colModel;
56916             var so = [];
56917             for(var i = 0; i < cm.config.length; i++ ) {
56918                 
56919                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56920                     continue; // dont' bother, it's not in sort list or being set.
56921                 }
56922                 
56923                 so.push(cm.config[i].dataIndex);
56924             };
56925             dm.sortOrder = so;
56926             dm.load(dm.lastOptions);
56927             
56928             
56929         }
56930         
56931     },
56932
56933     updateCell : function(dm, rowIndex, dataIndex){
56934         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56935         if(typeof colIndex == "undefined"){ // not present in grid
56936             return;
56937         }
56938         var cm = this.grid.colModel;
56939         var cell = this.getCell(rowIndex, colIndex);
56940         var cellText = this.getCellText(rowIndex, colIndex);
56941
56942         var p = {
56943             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56944             id : cm.getColumnId(colIndex),
56945             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56946         };
56947         var renderer = cm.getRenderer(colIndex);
56948         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56949         if(typeof val == "undefined" || val === "") {
56950             val = "&#160;";
56951         }
56952         cellText.innerHTML = val;
56953         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56954         this.syncRowHeights(rowIndex, rowIndex);
56955     },
56956
56957     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56958         var maxWidth = 0;
56959         if(this.grid.autoSizeHeaders){
56960             var h = this.getHeaderCellMeasure(colIndex);
56961             maxWidth = Math.max(maxWidth, h.scrollWidth);
56962         }
56963         var tb, index;
56964         if(this.cm.isLocked(colIndex)){
56965             tb = this.getLockedTable();
56966             index = colIndex;
56967         }else{
56968             tb = this.getBodyTable();
56969             index = colIndex - this.cm.getLockedCount();
56970         }
56971         if(tb && tb.rows){
56972             var rows = tb.rows;
56973             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56974             for(var i = 0; i < stopIndex; i++){
56975                 var cell = rows[i].childNodes[index].firstChild;
56976                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56977             }
56978         }
56979         return maxWidth + /*margin for error in IE*/ 5;
56980     },
56981     /**
56982      * Autofit a column to its content.
56983      * @param {Number} colIndex
56984      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56985      */
56986      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56987          if(this.cm.isHidden(colIndex)){
56988              return; // can't calc a hidden column
56989          }
56990         if(forceMinSize){
56991             var cid = this.cm.getColumnId(colIndex);
56992             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56993            if(this.grid.autoSizeHeaders){
56994                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56995            }
56996         }
56997         var newWidth = this.calcColumnWidth(colIndex);
56998         this.cm.setColumnWidth(colIndex,
56999             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57000         if(!suppressEvent){
57001             this.grid.fireEvent("columnresize", colIndex, newWidth);
57002         }
57003     },
57004
57005     /**
57006      * Autofits all columns to their content and then expands to fit any extra space in the grid
57007      */
57008      autoSizeColumns : function(){
57009         var cm = this.grid.colModel;
57010         var colCount = cm.getColumnCount();
57011         for(var i = 0; i < colCount; i++){
57012             this.autoSizeColumn(i, true, true);
57013         }
57014         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57015             this.fitColumns();
57016         }else{
57017             this.updateColumns();
57018             this.layout();
57019         }
57020     },
57021
57022     /**
57023      * Autofits all columns to the grid's width proportionate with their current size
57024      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57025      */
57026     fitColumns : function(reserveScrollSpace){
57027         var cm = this.grid.colModel;
57028         var colCount = cm.getColumnCount();
57029         var cols = [];
57030         var width = 0;
57031         var i, w;
57032         for (i = 0; i < colCount; i++){
57033             if(!cm.isHidden(i) && !cm.isFixed(i)){
57034                 w = cm.getColumnWidth(i);
57035                 cols.push(i);
57036                 cols.push(w);
57037                 width += w;
57038             }
57039         }
57040         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57041         if(reserveScrollSpace){
57042             avail -= 17;
57043         }
57044         var frac = (avail - cm.getTotalWidth())/width;
57045         while (cols.length){
57046             w = cols.pop();
57047             i = cols.pop();
57048             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57049         }
57050         this.updateColumns();
57051         this.layout();
57052     },
57053
57054     onRowSelect : function(rowIndex){
57055         var row = this.getRowComposite(rowIndex);
57056         row.addClass("x-grid-row-selected");
57057     },
57058
57059     onRowDeselect : function(rowIndex){
57060         var row = this.getRowComposite(rowIndex);
57061         row.removeClass("x-grid-row-selected");
57062     },
57063
57064     onCellSelect : function(row, col){
57065         var cell = this.getCell(row, col);
57066         if(cell){
57067             Roo.fly(cell).addClass("x-grid-cell-selected");
57068         }
57069     },
57070
57071     onCellDeselect : function(row, col){
57072         var cell = this.getCell(row, col);
57073         if(cell){
57074             Roo.fly(cell).removeClass("x-grid-cell-selected");
57075         }
57076     },
57077
57078     updateHeaderSortState : function(){
57079         
57080         // sort state can be single { field: xxx, direction : yyy}
57081         // or   { xxx=>ASC , yyy : DESC ..... }
57082         
57083         var mstate = {};
57084         if (!this.ds.multiSort) { 
57085             var state = this.ds.getSortState();
57086             if(!state){
57087                 return;
57088             }
57089             mstate[state.field] = state.direction;
57090             // FIXME... - this is not used here.. but might be elsewhere..
57091             this.sortState = state;
57092             
57093         } else {
57094             mstate = this.ds.sortToggle;
57095         }
57096         //remove existing sort classes..
57097         
57098         var sc = this.sortClasses;
57099         var hds = this.el.select(this.headerSelector).removeClass(sc);
57100         
57101         for(var f in mstate) {
57102         
57103             var sortColumn = this.cm.findColumnIndex(f);
57104             
57105             if(sortColumn != -1){
57106                 var sortDir = mstate[f];        
57107                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57108             }
57109         }
57110         
57111          
57112         
57113     },
57114
57115
57116     handleHeaderClick : function(g, index,e){
57117         
57118         Roo.log("header click");
57119         
57120         if (Roo.isTouch) {
57121             // touch events on header are handled by context
57122             this.handleHdCtx(g,index,e);
57123             return;
57124         }
57125         
57126         
57127         if(this.headersDisabled){
57128             return;
57129         }
57130         var dm = g.dataSource, cm = g.colModel;
57131         if(!cm.isSortable(index)){
57132             return;
57133         }
57134         g.stopEditing();
57135         
57136         if (dm.multiSort) {
57137             // update the sortOrder
57138             var so = [];
57139             for(var i = 0; i < cm.config.length; i++ ) {
57140                 
57141                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57142                     continue; // dont' bother, it's not in sort list or being set.
57143                 }
57144                 
57145                 so.push(cm.config[i].dataIndex);
57146             };
57147             dm.sortOrder = so;
57148         }
57149         
57150         
57151         dm.sort(cm.getDataIndex(index));
57152     },
57153
57154
57155     destroy : function(){
57156         if(this.colMenu){
57157             this.colMenu.removeAll();
57158             Roo.menu.MenuMgr.unregister(this.colMenu);
57159             this.colMenu.getEl().remove();
57160             delete this.colMenu;
57161         }
57162         if(this.hmenu){
57163             this.hmenu.removeAll();
57164             Roo.menu.MenuMgr.unregister(this.hmenu);
57165             this.hmenu.getEl().remove();
57166             delete this.hmenu;
57167         }
57168         if(this.grid.enableColumnMove){
57169             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57170             if(dds){
57171                 for(var dd in dds){
57172                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57173                         var elid = dds[dd].dragElId;
57174                         dds[dd].unreg();
57175                         Roo.get(elid).remove();
57176                     } else if(dds[dd].config.isTarget){
57177                         dds[dd].proxyTop.remove();
57178                         dds[dd].proxyBottom.remove();
57179                         dds[dd].unreg();
57180                     }
57181                     if(Roo.dd.DDM.locationCache[dd]){
57182                         delete Roo.dd.DDM.locationCache[dd];
57183                     }
57184                 }
57185                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57186             }
57187         }
57188         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57189         this.bind(null, null);
57190         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57191     },
57192
57193     handleLockChange : function(){
57194         this.refresh(true);
57195     },
57196
57197     onDenyColumnLock : function(){
57198
57199     },
57200
57201     onDenyColumnHide : function(){
57202
57203     },
57204
57205     handleHdMenuClick : function(item){
57206         var index = this.hdCtxIndex;
57207         var cm = this.cm, ds = this.ds;
57208         switch(item.id){
57209             case "asc":
57210                 ds.sort(cm.getDataIndex(index), "ASC");
57211                 break;
57212             case "desc":
57213                 ds.sort(cm.getDataIndex(index), "DESC");
57214                 break;
57215             case "lock":
57216                 var lc = cm.getLockedCount();
57217                 if(cm.getColumnCount(true) <= lc+1){
57218                     this.onDenyColumnLock();
57219                     return;
57220                 }
57221                 if(lc != index){
57222                     cm.setLocked(index, true, true);
57223                     cm.moveColumn(index, lc);
57224                     this.grid.fireEvent("columnmove", index, lc);
57225                 }else{
57226                     cm.setLocked(index, true);
57227                 }
57228             break;
57229             case "unlock":
57230                 var lc = cm.getLockedCount();
57231                 if((lc-1) != index){
57232                     cm.setLocked(index, false, true);
57233                     cm.moveColumn(index, lc-1);
57234                     this.grid.fireEvent("columnmove", index, lc-1);
57235                 }else{
57236                     cm.setLocked(index, false);
57237                 }
57238             break;
57239             case 'wider': // used to expand cols on touch..
57240             case 'narrow':
57241                 var cw = cm.getColumnWidth(index);
57242                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57243                 cw = Math.max(0, cw);
57244                 cw = Math.min(cw,4000);
57245                 cm.setColumnWidth(index, cw);
57246                 break;
57247                 
57248             default:
57249                 index = cm.getIndexById(item.id.substr(4));
57250                 if(index != -1){
57251                     if(item.checked && cm.getColumnCount(true) <= 1){
57252                         this.onDenyColumnHide();
57253                         return false;
57254                     }
57255                     cm.setHidden(index, item.checked);
57256                 }
57257         }
57258         return true;
57259     },
57260
57261     beforeColMenuShow : function(){
57262         var cm = this.cm,  colCount = cm.getColumnCount();
57263         this.colMenu.removeAll();
57264         for(var i = 0; i < colCount; i++){
57265             this.colMenu.add(new Roo.menu.CheckItem({
57266                 id: "col-"+cm.getColumnId(i),
57267                 text: cm.getColumnHeader(i),
57268                 checked: !cm.isHidden(i),
57269                 hideOnClick:false
57270             }));
57271         }
57272     },
57273
57274     handleHdCtx : function(g, index, e){
57275         e.stopEvent();
57276         var hd = this.getHeaderCell(index);
57277         this.hdCtxIndex = index;
57278         var ms = this.hmenu.items, cm = this.cm;
57279         ms.get("asc").setDisabled(!cm.isSortable(index));
57280         ms.get("desc").setDisabled(!cm.isSortable(index));
57281         if(this.grid.enableColLock !== false){
57282             ms.get("lock").setDisabled(cm.isLocked(index));
57283             ms.get("unlock").setDisabled(!cm.isLocked(index));
57284         }
57285         this.hmenu.show(hd, "tl-bl");
57286     },
57287
57288     handleHdOver : function(e){
57289         var hd = this.findHeaderCell(e.getTarget());
57290         if(hd && !this.headersDisabled){
57291             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57292                this.fly(hd).addClass("x-grid-hd-over");
57293             }
57294         }
57295     },
57296
57297     handleHdOut : function(e){
57298         var hd = this.findHeaderCell(e.getTarget());
57299         if(hd){
57300             this.fly(hd).removeClass("x-grid-hd-over");
57301         }
57302     },
57303
57304     handleSplitDblClick : function(e, t){
57305         var i = this.getCellIndex(t);
57306         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57307             this.autoSizeColumn(i, true);
57308             this.layout();
57309         }
57310     },
57311
57312     render : function(){
57313
57314         var cm = this.cm;
57315         var colCount = cm.getColumnCount();
57316
57317         if(this.grid.monitorWindowResize === true){
57318             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57319         }
57320         var header = this.renderHeaders();
57321         var body = this.templates.body.apply({rows:""});
57322         var html = this.templates.master.apply({
57323             lockedBody: body,
57324             body: body,
57325             lockedHeader: header[0],
57326             header: header[1]
57327         });
57328
57329         //this.updateColumns();
57330
57331         this.grid.getGridEl().dom.innerHTML = html;
57332
57333         this.initElements();
57334         
57335         // a kludge to fix the random scolling effect in webkit
57336         this.el.on("scroll", function() {
57337             this.el.dom.scrollTop=0; // hopefully not recursive..
57338         },this);
57339
57340         this.scroller.on("scroll", this.handleScroll, this);
57341         this.lockedBody.on("mousewheel", this.handleWheel, this);
57342         this.mainBody.on("mousewheel", this.handleWheel, this);
57343
57344         this.mainHd.on("mouseover", this.handleHdOver, this);
57345         this.mainHd.on("mouseout", this.handleHdOut, this);
57346         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57347                 {delegate: "."+this.splitClass});
57348
57349         this.lockedHd.on("mouseover", this.handleHdOver, this);
57350         this.lockedHd.on("mouseout", this.handleHdOut, this);
57351         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57352                 {delegate: "."+this.splitClass});
57353
57354         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57355             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57356         }
57357
57358         this.updateSplitters();
57359
57360         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57361             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57362             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57363         }
57364
57365         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57366             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57367             this.hmenu.add(
57368                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57369                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57370             );
57371             if(this.grid.enableColLock !== false){
57372                 this.hmenu.add('-',
57373                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57374                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57375                 );
57376             }
57377             if (Roo.isTouch) {
57378                  this.hmenu.add('-',
57379                     {id:"wider", text: this.columnsWiderText},
57380                     {id:"narrow", text: this.columnsNarrowText }
57381                 );
57382                 
57383                  
57384             }
57385             
57386             if(this.grid.enableColumnHide !== false){
57387
57388                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57389                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57390                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57391
57392                 this.hmenu.add('-',
57393                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57394                 );
57395             }
57396             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57397
57398             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57399         }
57400
57401         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57402             this.dd = new Roo.grid.GridDragZone(this.grid, {
57403                 ddGroup : this.grid.ddGroup || 'GridDD'
57404             });
57405             
57406         }
57407
57408         /*
57409         for(var i = 0; i < colCount; i++){
57410             if(cm.isHidden(i)){
57411                 this.hideColumn(i);
57412             }
57413             if(cm.config[i].align){
57414                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57415                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57416             }
57417         }*/
57418         
57419         this.updateHeaderSortState();
57420
57421         this.beforeInitialResize();
57422         this.layout(true);
57423
57424         // two part rendering gives faster view to the user
57425         this.renderPhase2.defer(1, this);
57426     },
57427
57428     renderPhase2 : function(){
57429         // render the rows now
57430         this.refresh();
57431         if(this.grid.autoSizeColumns){
57432             this.autoSizeColumns();
57433         }
57434     },
57435
57436     beforeInitialResize : function(){
57437
57438     },
57439
57440     onColumnSplitterMoved : function(i, w){
57441         this.userResized = true;
57442         var cm = this.grid.colModel;
57443         cm.setColumnWidth(i, w, true);
57444         var cid = cm.getColumnId(i);
57445         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57446         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57447         this.updateSplitters();
57448         this.layout();
57449         this.grid.fireEvent("columnresize", i, w);
57450     },
57451
57452     syncRowHeights : function(startIndex, endIndex){
57453         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57454             startIndex = startIndex || 0;
57455             var mrows = this.getBodyTable().rows;
57456             var lrows = this.getLockedTable().rows;
57457             var len = mrows.length-1;
57458             endIndex = Math.min(endIndex || len, len);
57459             for(var i = startIndex; i <= endIndex; i++){
57460                 var m = mrows[i], l = lrows[i];
57461                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57462                 m.style.height = l.style.height = h + "px";
57463             }
57464         }
57465     },
57466
57467     layout : function(initialRender, is2ndPass){
57468         var g = this.grid;
57469         var auto = g.autoHeight;
57470         var scrollOffset = 16;
57471         var c = g.getGridEl(), cm = this.cm,
57472                 expandCol = g.autoExpandColumn,
57473                 gv = this;
57474         //c.beginMeasure();
57475
57476         if(!c.dom.offsetWidth){ // display:none?
57477             if(initialRender){
57478                 this.lockedWrap.show();
57479                 this.mainWrap.show();
57480             }
57481             return;
57482         }
57483
57484         var hasLock = this.cm.isLocked(0);
57485
57486         var tbh = this.headerPanel.getHeight();
57487         var bbh = this.footerPanel.getHeight();
57488
57489         if(auto){
57490             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57491             var newHeight = ch + c.getBorderWidth("tb");
57492             if(g.maxHeight){
57493                 newHeight = Math.min(g.maxHeight, newHeight);
57494             }
57495             c.setHeight(newHeight);
57496         }
57497
57498         if(g.autoWidth){
57499             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57500         }
57501
57502         var s = this.scroller;
57503
57504         var csize = c.getSize(true);
57505
57506         this.el.setSize(csize.width, csize.height);
57507
57508         this.headerPanel.setWidth(csize.width);
57509         this.footerPanel.setWidth(csize.width);
57510
57511         var hdHeight = this.mainHd.getHeight();
57512         var vw = csize.width;
57513         var vh = csize.height - (tbh + bbh);
57514
57515         s.setSize(vw, vh);
57516
57517         var bt = this.getBodyTable();
57518         
57519         if(cm.getLockedCount() == cm.config.length){
57520             bt = this.getLockedTable();
57521         }
57522         
57523         var ltWidth = hasLock ?
57524                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57525
57526         var scrollHeight = bt.offsetHeight;
57527         var scrollWidth = ltWidth + bt.offsetWidth;
57528         var vscroll = false, hscroll = false;
57529
57530         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57531
57532         var lw = this.lockedWrap, mw = this.mainWrap;
57533         var lb = this.lockedBody, mb = this.mainBody;
57534
57535         setTimeout(function(){
57536             var t = s.dom.offsetTop;
57537             var w = s.dom.clientWidth,
57538                 h = s.dom.clientHeight;
57539
57540             lw.setTop(t);
57541             lw.setSize(ltWidth, h);
57542
57543             mw.setLeftTop(ltWidth, t);
57544             mw.setSize(w-ltWidth, h);
57545
57546             lb.setHeight(h-hdHeight);
57547             mb.setHeight(h-hdHeight);
57548
57549             if(is2ndPass !== true && !gv.userResized && expandCol){
57550                 // high speed resize without full column calculation
57551                 
57552                 var ci = cm.getIndexById(expandCol);
57553                 if (ci < 0) {
57554                     ci = cm.findColumnIndex(expandCol);
57555                 }
57556                 ci = Math.max(0, ci); // make sure it's got at least the first col.
57557                 var expandId = cm.getColumnId(ci);
57558                 var  tw = cm.getTotalWidth(false);
57559                 var currentWidth = cm.getColumnWidth(ci);
57560                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
57561                 if(currentWidth != cw){
57562                     cm.setColumnWidth(ci, cw, true);
57563                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57564                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57565                     gv.updateSplitters();
57566                     gv.layout(false, true);
57567                 }
57568             }
57569
57570             if(initialRender){
57571                 lw.show();
57572                 mw.show();
57573             }
57574             //c.endMeasure();
57575         }, 10);
57576     },
57577
57578     onWindowResize : function(){
57579         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
57580             return;
57581         }
57582         this.layout();
57583     },
57584
57585     appendFooter : function(parentEl){
57586         return null;
57587     },
57588
57589     sortAscText : "Sort Ascending",
57590     sortDescText : "Sort Descending",
57591     lockText : "Lock Column",
57592     unlockText : "Unlock Column",
57593     columnsText : "Columns",
57594  
57595     columnsWiderText : "Wider",
57596     columnsNarrowText : "Thinner"
57597 });
57598
57599
57600 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
57601     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
57602     this.proxy.el.addClass('x-grid3-col-dd');
57603 };
57604
57605 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
57606     handleMouseDown : function(e){
57607
57608     },
57609
57610     callHandleMouseDown : function(e){
57611         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
57612     }
57613 });
57614 /*
57615  * Based on:
57616  * Ext JS Library 1.1.1
57617  * Copyright(c) 2006-2007, Ext JS, LLC.
57618  *
57619  * Originally Released Under LGPL - original licence link has changed is not relivant.
57620  *
57621  * Fork - LGPL
57622  * <script type="text/javascript">
57623  */
57624  
57625 // private
57626 // This is a support class used internally by the Grid components
57627 Roo.grid.SplitDragZone = function(grid, hd, hd2){
57628     this.grid = grid;
57629     this.view = grid.getView();
57630     this.proxy = this.view.resizeProxy;
57631     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
57632         "gridSplitters" + this.grid.getGridEl().id, {
57633         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
57634     });
57635     this.setHandleElId(Roo.id(hd));
57636     this.setOuterHandleElId(Roo.id(hd2));
57637     this.scroll = false;
57638 };
57639 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
57640     fly: Roo.Element.fly,
57641
57642     b4StartDrag : function(x, y){
57643         this.view.headersDisabled = true;
57644         this.proxy.setHeight(this.view.mainWrap.getHeight());
57645         var w = this.cm.getColumnWidth(this.cellIndex);
57646         var minw = Math.max(w-this.grid.minColumnWidth, 0);
57647         this.resetConstraints();
57648         this.setXConstraint(minw, 1000);
57649         this.setYConstraint(0, 0);
57650         this.minX = x - minw;
57651         this.maxX = x + 1000;
57652         this.startPos = x;
57653         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
57654     },
57655
57656
57657     handleMouseDown : function(e){
57658         ev = Roo.EventObject.setEvent(e);
57659         var t = this.fly(ev.getTarget());
57660         if(t.hasClass("x-grid-split")){
57661             this.cellIndex = this.view.getCellIndex(t.dom);
57662             this.split = t.dom;
57663             this.cm = this.grid.colModel;
57664             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
57665                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
57666             }
57667         }
57668     },
57669
57670     endDrag : function(e){
57671         this.view.headersDisabled = false;
57672         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
57673         var diff = endX - this.startPos;
57674         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
57675     },
57676
57677     autoOffset : function(){
57678         this.setDelta(0,0);
57679     }
57680 });/*
57681  * Based on:
57682  * Ext JS Library 1.1.1
57683  * Copyright(c) 2006-2007, Ext JS, LLC.
57684  *
57685  * Originally Released Under LGPL - original licence link has changed is not relivant.
57686  *
57687  * Fork - LGPL
57688  * <script type="text/javascript">
57689  */
57690  
57691 // private
57692 // This is a support class used internally by the Grid components
57693 Roo.grid.GridDragZone = function(grid, config){
57694     this.view = grid.getView();
57695     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
57696     if(this.view.lockedBody){
57697         this.setHandleElId(Roo.id(this.view.mainBody.dom));
57698         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
57699     }
57700     this.scroll = false;
57701     this.grid = grid;
57702     this.ddel = document.createElement('div');
57703     this.ddel.className = 'x-grid-dd-wrap';
57704 };
57705
57706 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
57707     ddGroup : "GridDD",
57708
57709     getDragData : function(e){
57710         var t = Roo.lib.Event.getTarget(e);
57711         var rowIndex = this.view.findRowIndex(t);
57712         var sm = this.grid.selModel;
57713             
57714         //Roo.log(rowIndex);
57715         
57716         if (sm.getSelectedCell) {
57717             // cell selection..
57718             if (!sm.getSelectedCell()) {
57719                 return false;
57720             }
57721             if (rowIndex != sm.getSelectedCell()[0]) {
57722                 return false;
57723             }
57724         
57725         }
57726         
57727         if(rowIndex !== false){
57728             
57729             // if editorgrid.. 
57730             
57731             
57732             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
57733                
57734             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
57735               //  
57736             //}
57737             if (e.hasModifier()){
57738                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
57739             }
57740             
57741             Roo.log("getDragData");
57742             
57743             return {
57744                 grid: this.grid,
57745                 ddel: this.ddel,
57746                 rowIndex: rowIndex,
57747                 selections:sm.getSelections ? sm.getSelections() : (
57748                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
57749                 )
57750             };
57751         }
57752         return false;
57753     },
57754
57755     onInitDrag : function(e){
57756         var data = this.dragData;
57757         this.ddel.innerHTML = this.grid.getDragDropText();
57758         this.proxy.update(this.ddel);
57759         // fire start drag?
57760     },
57761
57762     afterRepair : function(){
57763         this.dragging = false;
57764     },
57765
57766     getRepairXY : function(e, data){
57767         return false;
57768     },
57769
57770     onEndDrag : function(data, e){
57771         // fire end drag?
57772     },
57773
57774     onValidDrop : function(dd, e, id){
57775         // fire drag drop?
57776         this.hideProxy();
57777     },
57778
57779     beforeInvalidDrop : function(e, id){
57780
57781     }
57782 });/*
57783  * Based on:
57784  * Ext JS Library 1.1.1
57785  * Copyright(c) 2006-2007, Ext JS, LLC.
57786  *
57787  * Originally Released Under LGPL - original licence link has changed is not relivant.
57788  *
57789  * Fork - LGPL
57790  * <script type="text/javascript">
57791  */
57792  
57793
57794 /**
57795  * @class Roo.grid.ColumnModel
57796  * @extends Roo.util.Observable
57797  * This is the default implementation of a ColumnModel used by the Grid. It defines
57798  * the columns in the grid.
57799  * <br>Usage:<br>
57800  <pre><code>
57801  var colModel = new Roo.grid.ColumnModel([
57802         {header: "Ticker", width: 60, sortable: true, locked: true},
57803         {header: "Company Name", width: 150, sortable: true},
57804         {header: "Market Cap.", width: 100, sortable: true},
57805         {header: "$ Sales", width: 100, sortable: true, renderer: money},
57806         {header: "Employees", width: 100, sortable: true, resizable: false}
57807  ]);
57808  </code></pre>
57809  * <p>
57810  
57811  * The config options listed for this class are options which may appear in each
57812  * individual column definition.
57813  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
57814  * @constructor
57815  * @param {Object} config An Array of column config objects. See this class's
57816  * config objects for details.
57817 */
57818 Roo.grid.ColumnModel = function(config){
57819         /**
57820      * The config passed into the constructor
57821      */
57822     this.config = config;
57823     this.lookup = {};
57824
57825     // if no id, create one
57826     // if the column does not have a dataIndex mapping,
57827     // map it to the order it is in the config
57828     for(var i = 0, len = config.length; i < len; i++){
57829         var c = config[i];
57830         if(typeof c.dataIndex == "undefined"){
57831             c.dataIndex = i;
57832         }
57833         if(typeof c.renderer == "string"){
57834             c.renderer = Roo.util.Format[c.renderer];
57835         }
57836         if(typeof c.id == "undefined"){
57837             c.id = Roo.id();
57838         }
57839         if(c.editor && c.editor.xtype){
57840             c.editor  = Roo.factory(c.editor, Roo.grid);
57841         }
57842         if(c.editor && c.editor.isFormField){
57843             c.editor = new Roo.grid.GridEditor(c.editor);
57844         }
57845         this.lookup[c.id] = c;
57846     }
57847
57848     /**
57849      * The width of columns which have no width specified (defaults to 100)
57850      * @type Number
57851      */
57852     this.defaultWidth = 100;
57853
57854     /**
57855      * Default sortable of columns which have no sortable specified (defaults to false)
57856      * @type Boolean
57857      */
57858     this.defaultSortable = false;
57859
57860     this.addEvents({
57861         /**
57862              * @event widthchange
57863              * Fires when the width of a column changes.
57864              * @param {ColumnModel} this
57865              * @param {Number} columnIndex The column index
57866              * @param {Number} newWidth The new width
57867              */
57868             "widthchange": true,
57869         /**
57870              * @event headerchange
57871              * Fires when the text of a header changes.
57872              * @param {ColumnModel} this
57873              * @param {Number} columnIndex The column index
57874              * @param {Number} newText The new header text
57875              */
57876             "headerchange": true,
57877         /**
57878              * @event hiddenchange
57879              * Fires when a column is hidden or "unhidden".
57880              * @param {ColumnModel} this
57881              * @param {Number} columnIndex The column index
57882              * @param {Boolean} hidden true if hidden, false otherwise
57883              */
57884             "hiddenchange": true,
57885             /**
57886          * @event columnmoved
57887          * Fires when a column is moved.
57888          * @param {ColumnModel} this
57889          * @param {Number} oldIndex
57890          * @param {Number} newIndex
57891          */
57892         "columnmoved" : true,
57893         /**
57894          * @event columlockchange
57895          * Fires when a column's locked state is changed
57896          * @param {ColumnModel} this
57897          * @param {Number} colIndex
57898          * @param {Boolean} locked true if locked
57899          */
57900         "columnlockchange" : true
57901     });
57902     Roo.grid.ColumnModel.superclass.constructor.call(this);
57903 };
57904 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57905     /**
57906      * @cfg {String} header The header text to display in the Grid view.
57907      */
57908     /**
57909      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57910      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57911      * specified, the column's index is used as an index into the Record's data Array.
57912      */
57913     /**
57914      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57915      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57916      */
57917     /**
57918      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57919      * Defaults to the value of the {@link #defaultSortable} property.
57920      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57921      */
57922     /**
57923      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57924      */
57925     /**
57926      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57927      */
57928     /**
57929      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57930      */
57931     /**
57932      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57933      */
57934     /**
57935      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57936      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57937      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57938      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57939      */
57940        /**
57941      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57942      */
57943     /**
57944      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57945      */
57946     /**
57947      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
57948      */
57949     /**
57950      * @cfg {String} cursor (Optional)
57951      */
57952     /**
57953      * @cfg {String} tooltip (Optional)
57954      */
57955     /**
57956      * @cfg {Number} xs (Optional)
57957      */
57958     /**
57959      * @cfg {Number} sm (Optional)
57960      */
57961     /**
57962      * @cfg {Number} md (Optional)
57963      */
57964     /**
57965      * @cfg {Number} lg (Optional)
57966      */
57967     /**
57968      * Returns the id of the column at the specified index.
57969      * @param {Number} index The column index
57970      * @return {String} the id
57971      */
57972     getColumnId : function(index){
57973         return this.config[index].id;
57974     },
57975
57976     /**
57977      * Returns the column for a specified id.
57978      * @param {String} id The column id
57979      * @return {Object} the column
57980      */
57981     getColumnById : function(id){
57982         return this.lookup[id];
57983     },
57984
57985     
57986     /**
57987      * Returns the column for a specified dataIndex.
57988      * @param {String} dataIndex The column dataIndex
57989      * @return {Object|Boolean} the column or false if not found
57990      */
57991     getColumnByDataIndex: function(dataIndex){
57992         var index = this.findColumnIndex(dataIndex);
57993         return index > -1 ? this.config[index] : false;
57994     },
57995     
57996     /**
57997      * Returns the index for a specified column id.
57998      * @param {String} id The column id
57999      * @return {Number} the index, or -1 if not found
58000      */
58001     getIndexById : function(id){
58002         for(var i = 0, len = this.config.length; i < len; i++){
58003             if(this.config[i].id == id){
58004                 return i;
58005             }
58006         }
58007         return -1;
58008     },
58009     
58010     /**
58011      * Returns the index for a specified column dataIndex.
58012      * @param {String} dataIndex The column dataIndex
58013      * @return {Number} the index, or -1 if not found
58014      */
58015     
58016     findColumnIndex : function(dataIndex){
58017         for(var i = 0, len = this.config.length; i < len; i++){
58018             if(this.config[i].dataIndex == dataIndex){
58019                 return i;
58020             }
58021         }
58022         return -1;
58023     },
58024     
58025     
58026     moveColumn : function(oldIndex, newIndex){
58027         var c = this.config[oldIndex];
58028         this.config.splice(oldIndex, 1);
58029         this.config.splice(newIndex, 0, c);
58030         this.dataMap = null;
58031         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58032     },
58033
58034     isLocked : function(colIndex){
58035         return this.config[colIndex].locked === true;
58036     },
58037
58038     setLocked : function(colIndex, value, suppressEvent){
58039         if(this.isLocked(colIndex) == value){
58040             return;
58041         }
58042         this.config[colIndex].locked = value;
58043         if(!suppressEvent){
58044             this.fireEvent("columnlockchange", this, colIndex, value);
58045         }
58046     },
58047
58048     getTotalLockedWidth : function(){
58049         var totalWidth = 0;
58050         for(var i = 0; i < this.config.length; i++){
58051             if(this.isLocked(i) && !this.isHidden(i)){
58052                 this.totalWidth += this.getColumnWidth(i);
58053             }
58054         }
58055         return totalWidth;
58056     },
58057
58058     getLockedCount : function(){
58059         for(var i = 0, len = this.config.length; i < len; i++){
58060             if(!this.isLocked(i)){
58061                 return i;
58062             }
58063         }
58064         
58065         return this.config.length;
58066     },
58067
58068     /**
58069      * Returns the number of columns.
58070      * @return {Number}
58071      */
58072     getColumnCount : function(visibleOnly){
58073         if(visibleOnly === true){
58074             var c = 0;
58075             for(var i = 0, len = this.config.length; i < len; i++){
58076                 if(!this.isHidden(i)){
58077                     c++;
58078                 }
58079             }
58080             return c;
58081         }
58082         return this.config.length;
58083     },
58084
58085     /**
58086      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58087      * @param {Function} fn
58088      * @param {Object} scope (optional)
58089      * @return {Array} result
58090      */
58091     getColumnsBy : function(fn, scope){
58092         var r = [];
58093         for(var i = 0, len = this.config.length; i < len; i++){
58094             var c = this.config[i];
58095             if(fn.call(scope||this, c, i) === true){
58096                 r[r.length] = c;
58097             }
58098         }
58099         return r;
58100     },
58101
58102     /**
58103      * Returns true if the specified column is sortable.
58104      * @param {Number} col The column index
58105      * @return {Boolean}
58106      */
58107     isSortable : function(col){
58108         if(typeof this.config[col].sortable == "undefined"){
58109             return this.defaultSortable;
58110         }
58111         return this.config[col].sortable;
58112     },
58113
58114     /**
58115      * Returns the rendering (formatting) function defined for the column.
58116      * @param {Number} col The column index.
58117      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58118      */
58119     getRenderer : function(col){
58120         if(!this.config[col].renderer){
58121             return Roo.grid.ColumnModel.defaultRenderer;
58122         }
58123         return this.config[col].renderer;
58124     },
58125
58126     /**
58127      * Sets the rendering (formatting) function for a column.
58128      * @param {Number} col The column index
58129      * @param {Function} fn The function to use to process the cell's raw data
58130      * to return HTML markup for the grid view. The render function is called with
58131      * the following parameters:<ul>
58132      * <li>Data value.</li>
58133      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58134      * <li>css A CSS style string to apply to the table cell.</li>
58135      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58136      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58137      * <li>Row index</li>
58138      * <li>Column index</li>
58139      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58140      */
58141     setRenderer : function(col, fn){
58142         this.config[col].renderer = fn;
58143     },
58144
58145     /**
58146      * Returns the width for the specified column.
58147      * @param {Number} col The column index
58148      * @return {Number}
58149      */
58150     getColumnWidth : function(col){
58151         return this.config[col].width * 1 || this.defaultWidth;
58152     },
58153
58154     /**
58155      * Sets the width for a column.
58156      * @param {Number} col The column index
58157      * @param {Number} width The new width
58158      */
58159     setColumnWidth : function(col, width, suppressEvent){
58160         this.config[col].width = width;
58161         this.totalWidth = null;
58162         if(!suppressEvent){
58163              this.fireEvent("widthchange", this, col, width);
58164         }
58165     },
58166
58167     /**
58168      * Returns the total width of all columns.
58169      * @param {Boolean} includeHidden True to include hidden column widths
58170      * @return {Number}
58171      */
58172     getTotalWidth : function(includeHidden){
58173         if(!this.totalWidth){
58174             this.totalWidth = 0;
58175             for(var i = 0, len = this.config.length; i < len; i++){
58176                 if(includeHidden || !this.isHidden(i)){
58177                     this.totalWidth += this.getColumnWidth(i);
58178                 }
58179             }
58180         }
58181         return this.totalWidth;
58182     },
58183
58184     /**
58185      * Returns the header for the specified column.
58186      * @param {Number} col The column index
58187      * @return {String}
58188      */
58189     getColumnHeader : function(col){
58190         return this.config[col].header;
58191     },
58192
58193     /**
58194      * Sets the header for a column.
58195      * @param {Number} col The column index
58196      * @param {String} header The new header
58197      */
58198     setColumnHeader : function(col, header){
58199         this.config[col].header = header;
58200         this.fireEvent("headerchange", this, col, header);
58201     },
58202
58203     /**
58204      * Returns the tooltip for the specified column.
58205      * @param {Number} col The column index
58206      * @return {String}
58207      */
58208     getColumnTooltip : function(col){
58209             return this.config[col].tooltip;
58210     },
58211     /**
58212      * Sets the tooltip for a column.
58213      * @param {Number} col The column index
58214      * @param {String} tooltip The new tooltip
58215      */
58216     setColumnTooltip : function(col, tooltip){
58217             this.config[col].tooltip = tooltip;
58218     },
58219
58220     /**
58221      * Returns the dataIndex for the specified column.
58222      * @param {Number} col The column index
58223      * @return {Number}
58224      */
58225     getDataIndex : function(col){
58226         return this.config[col].dataIndex;
58227     },
58228
58229     /**
58230      * Sets the dataIndex for a column.
58231      * @param {Number} col The column index
58232      * @param {Number} dataIndex The new dataIndex
58233      */
58234     setDataIndex : function(col, dataIndex){
58235         this.config[col].dataIndex = dataIndex;
58236     },
58237
58238     
58239     
58240     /**
58241      * Returns true if the cell is editable.
58242      * @param {Number} colIndex The column index
58243      * @param {Number} rowIndex The row index - this is nto actually used..?
58244      * @return {Boolean}
58245      */
58246     isCellEditable : function(colIndex, rowIndex){
58247         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58248     },
58249
58250     /**
58251      * Returns the editor defined for the cell/column.
58252      * return false or null to disable editing.
58253      * @param {Number} colIndex The column index
58254      * @param {Number} rowIndex The row index
58255      * @return {Object}
58256      */
58257     getCellEditor : function(colIndex, rowIndex){
58258         return this.config[colIndex].editor;
58259     },
58260
58261     /**
58262      * Sets if a column is editable.
58263      * @param {Number} col The column index
58264      * @param {Boolean} editable True if the column is editable
58265      */
58266     setEditable : function(col, editable){
58267         this.config[col].editable = editable;
58268     },
58269
58270
58271     /**
58272      * Returns true if the column is hidden.
58273      * @param {Number} colIndex The column index
58274      * @return {Boolean}
58275      */
58276     isHidden : function(colIndex){
58277         return this.config[colIndex].hidden;
58278     },
58279
58280
58281     /**
58282      * Returns true if the column width cannot be changed
58283      */
58284     isFixed : function(colIndex){
58285         return this.config[colIndex].fixed;
58286     },
58287
58288     /**
58289      * Returns true if the column can be resized
58290      * @return {Boolean}
58291      */
58292     isResizable : function(colIndex){
58293         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58294     },
58295     /**
58296      * Sets if a column is hidden.
58297      * @param {Number} colIndex The column index
58298      * @param {Boolean} hidden True if the column is hidden
58299      */
58300     setHidden : function(colIndex, hidden){
58301         this.config[colIndex].hidden = hidden;
58302         this.totalWidth = null;
58303         this.fireEvent("hiddenchange", this, colIndex, hidden);
58304     },
58305
58306     /**
58307      * Sets the editor for a column.
58308      * @param {Number} col The column index
58309      * @param {Object} editor The editor object
58310      */
58311     setEditor : function(col, editor){
58312         this.config[col].editor = editor;
58313     }
58314 });
58315
58316 Roo.grid.ColumnModel.defaultRenderer = function(value)
58317 {
58318     if(typeof value == "object") {
58319         return value;
58320     }
58321         if(typeof value == "string" && value.length < 1){
58322             return "&#160;";
58323         }
58324     
58325         return String.format("{0}", value);
58326 };
58327
58328 // Alias for backwards compatibility
58329 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58330 /*
58331  * Based on:
58332  * Ext JS Library 1.1.1
58333  * Copyright(c) 2006-2007, Ext JS, LLC.
58334  *
58335  * Originally Released Under LGPL - original licence link has changed is not relivant.
58336  *
58337  * Fork - LGPL
58338  * <script type="text/javascript">
58339  */
58340
58341 /**
58342  * @class Roo.grid.AbstractSelectionModel
58343  * @extends Roo.util.Observable
58344  * Abstract base class for grid SelectionModels.  It provides the interface that should be
58345  * implemented by descendant classes.  This class should not be directly instantiated.
58346  * @constructor
58347  */
58348 Roo.grid.AbstractSelectionModel = function(){
58349     this.locked = false;
58350     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58351 };
58352
58353 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58354     /** @ignore Called by the grid automatically. Do not call directly. */
58355     init : function(grid){
58356         this.grid = grid;
58357         this.initEvents();
58358     },
58359
58360     /**
58361      * Locks the selections.
58362      */
58363     lock : function(){
58364         this.locked = true;
58365     },
58366
58367     /**
58368      * Unlocks the selections.
58369      */
58370     unlock : function(){
58371         this.locked = false;
58372     },
58373
58374     /**
58375      * Returns true if the selections are locked.
58376      * @return {Boolean}
58377      */
58378     isLocked : function(){
58379         return this.locked;
58380     }
58381 });/*
58382  * Based on:
58383  * Ext JS Library 1.1.1
58384  * Copyright(c) 2006-2007, Ext JS, LLC.
58385  *
58386  * Originally Released Under LGPL - original licence link has changed is not relivant.
58387  *
58388  * Fork - LGPL
58389  * <script type="text/javascript">
58390  */
58391 /**
58392  * @extends Roo.grid.AbstractSelectionModel
58393  * @class Roo.grid.RowSelectionModel
58394  * The default SelectionModel used by {@link Roo.grid.Grid}.
58395  * It supports multiple selections and keyboard selection/navigation. 
58396  * @constructor
58397  * @param {Object} config
58398  */
58399 Roo.grid.RowSelectionModel = function(config){
58400     Roo.apply(this, config);
58401     this.selections = new Roo.util.MixedCollection(false, function(o){
58402         return o.id;
58403     });
58404
58405     this.last = false;
58406     this.lastActive = false;
58407
58408     this.addEvents({
58409         /**
58410              * @event selectionchange
58411              * Fires when the selection changes
58412              * @param {SelectionModel} this
58413              */
58414             "selectionchange" : true,
58415         /**
58416              * @event afterselectionchange
58417              * Fires after the selection changes (eg. by key press or clicking)
58418              * @param {SelectionModel} this
58419              */
58420             "afterselectionchange" : true,
58421         /**
58422              * @event beforerowselect
58423              * Fires when a row is selected being selected, return false to cancel.
58424              * @param {SelectionModel} this
58425              * @param {Number} rowIndex The selected index
58426              * @param {Boolean} keepExisting False if other selections will be cleared
58427              */
58428             "beforerowselect" : true,
58429         /**
58430              * @event rowselect
58431              * Fires when a row is selected.
58432              * @param {SelectionModel} this
58433              * @param {Number} rowIndex The selected index
58434              * @param {Roo.data.Record} r The record
58435              */
58436             "rowselect" : true,
58437         /**
58438              * @event rowdeselect
58439              * Fires when a row is deselected.
58440              * @param {SelectionModel} this
58441              * @param {Number} rowIndex The selected index
58442              */
58443         "rowdeselect" : true
58444     });
58445     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
58446     this.locked = false;
58447 };
58448
58449 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
58450     /**
58451      * @cfg {Boolean} singleSelect
58452      * True to allow selection of only one row at a time (defaults to false)
58453      */
58454     singleSelect : false,
58455
58456     // private
58457     initEvents : function(){
58458
58459         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
58460             this.grid.on("mousedown", this.handleMouseDown, this);
58461         }else{ // allow click to work like normal
58462             this.grid.on("rowclick", this.handleDragableRowClick, this);
58463         }
58464
58465         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
58466             "up" : function(e){
58467                 if(!e.shiftKey){
58468                     this.selectPrevious(e.shiftKey);
58469                 }else if(this.last !== false && this.lastActive !== false){
58470                     var last = this.last;
58471                     this.selectRange(this.last,  this.lastActive-1);
58472                     this.grid.getView().focusRow(this.lastActive);
58473                     if(last !== false){
58474                         this.last = last;
58475                     }
58476                 }else{
58477                     this.selectFirstRow();
58478                 }
58479                 this.fireEvent("afterselectionchange", this);
58480             },
58481             "down" : function(e){
58482                 if(!e.shiftKey){
58483                     this.selectNext(e.shiftKey);
58484                 }else if(this.last !== false && this.lastActive !== false){
58485                     var last = this.last;
58486                     this.selectRange(this.last,  this.lastActive+1);
58487                     this.grid.getView().focusRow(this.lastActive);
58488                     if(last !== false){
58489                         this.last = last;
58490                     }
58491                 }else{
58492                     this.selectFirstRow();
58493                 }
58494                 this.fireEvent("afterselectionchange", this);
58495             },
58496             scope: this
58497         });
58498
58499         var view = this.grid.view;
58500         view.on("refresh", this.onRefresh, this);
58501         view.on("rowupdated", this.onRowUpdated, this);
58502         view.on("rowremoved", this.onRemove, this);
58503     },
58504
58505     // private
58506     onRefresh : function(){
58507         var ds = this.grid.dataSource, i, v = this.grid.view;
58508         var s = this.selections;
58509         s.each(function(r){
58510             if((i = ds.indexOfId(r.id)) != -1){
58511                 v.onRowSelect(i);
58512                 s.add(ds.getAt(i)); // updating the selection relate data
58513             }else{
58514                 s.remove(r);
58515             }
58516         });
58517     },
58518
58519     // private
58520     onRemove : function(v, index, r){
58521         this.selections.remove(r);
58522     },
58523
58524     // private
58525     onRowUpdated : function(v, index, r){
58526         if(this.isSelected(r)){
58527             v.onRowSelect(index);
58528         }
58529     },
58530
58531     /**
58532      * Select records.
58533      * @param {Array} records The records to select
58534      * @param {Boolean} keepExisting (optional) True to keep existing selections
58535      */
58536     selectRecords : function(records, keepExisting){
58537         if(!keepExisting){
58538             this.clearSelections();
58539         }
58540         var ds = this.grid.dataSource;
58541         for(var i = 0, len = records.length; i < len; i++){
58542             this.selectRow(ds.indexOf(records[i]), true);
58543         }
58544     },
58545
58546     /**
58547      * Gets the number of selected rows.
58548      * @return {Number}
58549      */
58550     getCount : function(){
58551         return this.selections.length;
58552     },
58553
58554     /**
58555      * Selects the first row in the grid.
58556      */
58557     selectFirstRow : function(){
58558         this.selectRow(0);
58559     },
58560
58561     /**
58562      * Select the last row.
58563      * @param {Boolean} keepExisting (optional) True to keep existing selections
58564      */
58565     selectLastRow : function(keepExisting){
58566         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
58567     },
58568
58569     /**
58570      * Selects the row immediately following the last selected row.
58571      * @param {Boolean} keepExisting (optional) True to keep existing selections
58572      */
58573     selectNext : function(keepExisting){
58574         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
58575             this.selectRow(this.last+1, keepExisting);
58576             this.grid.getView().focusRow(this.last);
58577         }
58578     },
58579
58580     /**
58581      * Selects the row that precedes the last selected row.
58582      * @param {Boolean} keepExisting (optional) True to keep existing selections
58583      */
58584     selectPrevious : function(keepExisting){
58585         if(this.last){
58586             this.selectRow(this.last-1, keepExisting);
58587             this.grid.getView().focusRow(this.last);
58588         }
58589     },
58590
58591     /**
58592      * Returns the selected records
58593      * @return {Array} Array of selected records
58594      */
58595     getSelections : function(){
58596         return [].concat(this.selections.items);
58597     },
58598
58599     /**
58600      * Returns the first selected record.
58601      * @return {Record}
58602      */
58603     getSelected : function(){
58604         return this.selections.itemAt(0);
58605     },
58606
58607
58608     /**
58609      * Clears all selections.
58610      */
58611     clearSelections : function(fast){
58612         if(this.locked) {
58613             return;
58614         }
58615         if(fast !== true){
58616             var ds = this.grid.dataSource;
58617             var s = this.selections;
58618             s.each(function(r){
58619                 this.deselectRow(ds.indexOfId(r.id));
58620             }, this);
58621             s.clear();
58622         }else{
58623             this.selections.clear();
58624         }
58625         this.last = false;
58626     },
58627
58628
58629     /**
58630      * Selects all rows.
58631      */
58632     selectAll : function(){
58633         if(this.locked) {
58634             return;
58635         }
58636         this.selections.clear();
58637         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
58638             this.selectRow(i, true);
58639         }
58640     },
58641
58642     /**
58643      * Returns True if there is a selection.
58644      * @return {Boolean}
58645      */
58646     hasSelection : function(){
58647         return this.selections.length > 0;
58648     },
58649
58650     /**
58651      * Returns True if the specified row is selected.
58652      * @param {Number/Record} record The record or index of the record to check
58653      * @return {Boolean}
58654      */
58655     isSelected : function(index){
58656         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
58657         return (r && this.selections.key(r.id) ? true : false);
58658     },
58659
58660     /**
58661      * Returns True if the specified record id is selected.
58662      * @param {String} id The id of record to check
58663      * @return {Boolean}
58664      */
58665     isIdSelected : function(id){
58666         return (this.selections.key(id) ? true : false);
58667     },
58668
58669     // private
58670     handleMouseDown : function(e, t){
58671         var view = this.grid.getView(), rowIndex;
58672         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
58673             return;
58674         };
58675         if(e.shiftKey && this.last !== false){
58676             var last = this.last;
58677             this.selectRange(last, rowIndex, e.ctrlKey);
58678             this.last = last; // reset the last
58679             view.focusRow(rowIndex);
58680         }else{
58681             var isSelected = this.isSelected(rowIndex);
58682             if(e.button !== 0 && isSelected){
58683                 view.focusRow(rowIndex);
58684             }else if(e.ctrlKey && isSelected){
58685                 this.deselectRow(rowIndex);
58686             }else if(!isSelected){
58687                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
58688                 view.focusRow(rowIndex);
58689             }
58690         }
58691         this.fireEvent("afterselectionchange", this);
58692     },
58693     // private
58694     handleDragableRowClick :  function(grid, rowIndex, e) 
58695     {
58696         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
58697             this.selectRow(rowIndex, false);
58698             grid.view.focusRow(rowIndex);
58699              this.fireEvent("afterselectionchange", this);
58700         }
58701     },
58702     
58703     /**
58704      * Selects multiple rows.
58705      * @param {Array} rows Array of the indexes of the row to select
58706      * @param {Boolean} keepExisting (optional) True to keep existing selections
58707      */
58708     selectRows : function(rows, keepExisting){
58709         if(!keepExisting){
58710             this.clearSelections();
58711         }
58712         for(var i = 0, len = rows.length; i < len; i++){
58713             this.selectRow(rows[i], true);
58714         }
58715     },
58716
58717     /**
58718      * Selects a range of rows. All rows in between startRow and endRow are also selected.
58719      * @param {Number} startRow The index of the first row in the range
58720      * @param {Number} endRow The index of the last row in the range
58721      * @param {Boolean} keepExisting (optional) True to retain existing selections
58722      */
58723     selectRange : function(startRow, endRow, keepExisting){
58724         if(this.locked) {
58725             return;
58726         }
58727         if(!keepExisting){
58728             this.clearSelections();
58729         }
58730         if(startRow <= endRow){
58731             for(var i = startRow; i <= endRow; i++){
58732                 this.selectRow(i, true);
58733             }
58734         }else{
58735             for(var i = startRow; i >= endRow; i--){
58736                 this.selectRow(i, true);
58737             }
58738         }
58739     },
58740
58741     /**
58742      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
58743      * @param {Number} startRow The index of the first row in the range
58744      * @param {Number} endRow The index of the last row in the range
58745      */
58746     deselectRange : function(startRow, endRow, preventViewNotify){
58747         if(this.locked) {
58748             return;
58749         }
58750         for(var i = startRow; i <= endRow; i++){
58751             this.deselectRow(i, preventViewNotify);
58752         }
58753     },
58754
58755     /**
58756      * Selects a row.
58757      * @param {Number} row The index of the row to select
58758      * @param {Boolean} keepExisting (optional) True to keep existing selections
58759      */
58760     selectRow : function(index, keepExisting, preventViewNotify){
58761         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
58762             return;
58763         }
58764         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
58765             if(!keepExisting || this.singleSelect){
58766                 this.clearSelections();
58767             }
58768             var r = this.grid.dataSource.getAt(index);
58769             this.selections.add(r);
58770             this.last = this.lastActive = index;
58771             if(!preventViewNotify){
58772                 this.grid.getView().onRowSelect(index);
58773             }
58774             this.fireEvent("rowselect", this, index, r);
58775             this.fireEvent("selectionchange", this);
58776         }
58777     },
58778
58779     /**
58780      * Deselects a row.
58781      * @param {Number} row The index of the row to deselect
58782      */
58783     deselectRow : function(index, preventViewNotify){
58784         if(this.locked) {
58785             return;
58786         }
58787         if(this.last == index){
58788             this.last = false;
58789         }
58790         if(this.lastActive == index){
58791             this.lastActive = false;
58792         }
58793         var r = this.grid.dataSource.getAt(index);
58794         this.selections.remove(r);
58795         if(!preventViewNotify){
58796             this.grid.getView().onRowDeselect(index);
58797         }
58798         this.fireEvent("rowdeselect", this, index);
58799         this.fireEvent("selectionchange", this);
58800     },
58801
58802     // private
58803     restoreLast : function(){
58804         if(this._last){
58805             this.last = this._last;
58806         }
58807     },
58808
58809     // private
58810     acceptsNav : function(row, col, cm){
58811         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58812     },
58813
58814     // private
58815     onEditorKey : function(field, e){
58816         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
58817         if(k == e.TAB){
58818             e.stopEvent();
58819             ed.completeEdit();
58820             if(e.shiftKey){
58821                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58822             }else{
58823                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58824             }
58825         }else if(k == e.ENTER && !e.ctrlKey){
58826             e.stopEvent();
58827             ed.completeEdit();
58828             if(e.shiftKey){
58829                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
58830             }else{
58831                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
58832             }
58833         }else if(k == e.ESC){
58834             ed.cancelEdit();
58835         }
58836         if(newCell){
58837             g.startEditing(newCell[0], newCell[1]);
58838         }
58839     }
58840 });/*
58841  * Based on:
58842  * Ext JS Library 1.1.1
58843  * Copyright(c) 2006-2007, Ext JS, LLC.
58844  *
58845  * Originally Released Under LGPL - original licence link has changed is not relivant.
58846  *
58847  * Fork - LGPL
58848  * <script type="text/javascript">
58849  */
58850 /**
58851  * @class Roo.grid.CellSelectionModel
58852  * @extends Roo.grid.AbstractSelectionModel
58853  * This class provides the basic implementation for cell selection in a grid.
58854  * @constructor
58855  * @param {Object} config The object containing the configuration of this model.
58856  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
58857  */
58858 Roo.grid.CellSelectionModel = function(config){
58859     Roo.apply(this, config);
58860
58861     this.selection = null;
58862
58863     this.addEvents({
58864         /**
58865              * @event beforerowselect
58866              * Fires before a cell is selected.
58867              * @param {SelectionModel} this
58868              * @param {Number} rowIndex The selected row index
58869              * @param {Number} colIndex The selected cell index
58870              */
58871             "beforecellselect" : true,
58872         /**
58873              * @event cellselect
58874              * Fires when a cell is selected.
58875              * @param {SelectionModel} this
58876              * @param {Number} rowIndex The selected row index
58877              * @param {Number} colIndex The selected cell index
58878              */
58879             "cellselect" : true,
58880         /**
58881              * @event selectionchange
58882              * Fires when the active selection changes.
58883              * @param {SelectionModel} this
58884              * @param {Object} selection null for no selection or an object (o) with two properties
58885                 <ul>
58886                 <li>o.record: the record object for the row the selection is in</li>
58887                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58888                 </ul>
58889              */
58890             "selectionchange" : true,
58891         /**
58892              * @event tabend
58893              * Fires when the tab (or enter) was pressed on the last editable cell
58894              * You can use this to trigger add new row.
58895              * @param {SelectionModel} this
58896              */
58897             "tabend" : true,
58898          /**
58899              * @event beforeeditnext
58900              * Fires before the next editable sell is made active
58901              * You can use this to skip to another cell or fire the tabend
58902              *    if you set cell to false
58903              * @param {Object} eventdata object : { cell : [ row, col ] } 
58904              */
58905             "beforeeditnext" : true
58906     });
58907     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58908 };
58909
58910 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58911     
58912     enter_is_tab: false,
58913
58914     /** @ignore */
58915     initEvents : function(){
58916         this.grid.on("mousedown", this.handleMouseDown, this);
58917         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58918         var view = this.grid.view;
58919         view.on("refresh", this.onViewChange, this);
58920         view.on("rowupdated", this.onRowUpdated, this);
58921         view.on("beforerowremoved", this.clearSelections, this);
58922         view.on("beforerowsinserted", this.clearSelections, this);
58923         if(this.grid.isEditor){
58924             this.grid.on("beforeedit", this.beforeEdit,  this);
58925         }
58926     },
58927
58928         //private
58929     beforeEdit : function(e){
58930         this.select(e.row, e.column, false, true, e.record);
58931     },
58932
58933         //private
58934     onRowUpdated : function(v, index, r){
58935         if(this.selection && this.selection.record == r){
58936             v.onCellSelect(index, this.selection.cell[1]);
58937         }
58938     },
58939
58940         //private
58941     onViewChange : function(){
58942         this.clearSelections(true);
58943     },
58944
58945         /**
58946          * Returns the currently selected cell,.
58947          * @return {Array} The selected cell (row, column) or null if none selected.
58948          */
58949     getSelectedCell : function(){
58950         return this.selection ? this.selection.cell : null;
58951     },
58952
58953     /**
58954      * Clears all selections.
58955      * @param {Boolean} true to prevent the gridview from being notified about the change.
58956      */
58957     clearSelections : function(preventNotify){
58958         var s = this.selection;
58959         if(s){
58960             if(preventNotify !== true){
58961                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58962             }
58963             this.selection = null;
58964             this.fireEvent("selectionchange", this, null);
58965         }
58966     },
58967
58968     /**
58969      * Returns true if there is a selection.
58970      * @return {Boolean}
58971      */
58972     hasSelection : function(){
58973         return this.selection ? true : false;
58974     },
58975
58976     /** @ignore */
58977     handleMouseDown : function(e, t){
58978         var v = this.grid.getView();
58979         if(this.isLocked()){
58980             return;
58981         };
58982         var row = v.findRowIndex(t);
58983         var cell = v.findCellIndex(t);
58984         if(row !== false && cell !== false){
58985             this.select(row, cell);
58986         }
58987     },
58988
58989     /**
58990      * Selects a cell.
58991      * @param {Number} rowIndex
58992      * @param {Number} collIndex
58993      */
58994     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58995         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58996             this.clearSelections();
58997             r = r || this.grid.dataSource.getAt(rowIndex);
58998             this.selection = {
58999                 record : r,
59000                 cell : [rowIndex, colIndex]
59001             };
59002             if(!preventViewNotify){
59003                 var v = this.grid.getView();
59004                 v.onCellSelect(rowIndex, colIndex);
59005                 if(preventFocus !== true){
59006                     v.focusCell(rowIndex, colIndex);
59007                 }
59008             }
59009             this.fireEvent("cellselect", this, rowIndex, colIndex);
59010             this.fireEvent("selectionchange", this, this.selection);
59011         }
59012     },
59013
59014         //private
59015     isSelectable : function(rowIndex, colIndex, cm){
59016         return !cm.isHidden(colIndex);
59017     },
59018
59019     /** @ignore */
59020     handleKeyDown : function(e){
59021         //Roo.log('Cell Sel Model handleKeyDown');
59022         if(!e.isNavKeyPress()){
59023             return;
59024         }
59025         var g = this.grid, s = this.selection;
59026         if(!s){
59027             e.stopEvent();
59028             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59029             if(cell){
59030                 this.select(cell[0], cell[1]);
59031             }
59032             return;
59033         }
59034         var sm = this;
59035         var walk = function(row, col, step){
59036             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59037         };
59038         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59039         var newCell;
59040
59041       
59042
59043         switch(k){
59044             case e.TAB:
59045                 // handled by onEditorKey
59046                 if (g.isEditor && g.editing) {
59047                     return;
59048                 }
59049                 if(e.shiftKey) {
59050                     newCell = walk(r, c-1, -1);
59051                 } else {
59052                     newCell = walk(r, c+1, 1);
59053                 }
59054                 break;
59055             
59056             case e.DOWN:
59057                newCell = walk(r+1, c, 1);
59058                 break;
59059             
59060             case e.UP:
59061                 newCell = walk(r-1, c, -1);
59062                 break;
59063             
59064             case e.RIGHT:
59065                 newCell = walk(r, c+1, 1);
59066                 break;
59067             
59068             case e.LEFT:
59069                 newCell = walk(r, c-1, -1);
59070                 break;
59071             
59072             case e.ENTER:
59073                 
59074                 if(g.isEditor && !g.editing){
59075                    g.startEditing(r, c);
59076                    e.stopEvent();
59077                    return;
59078                 }
59079                 
59080                 
59081              break;
59082         };
59083         if(newCell){
59084             this.select(newCell[0], newCell[1]);
59085             e.stopEvent();
59086             
59087         }
59088     },
59089
59090     acceptsNav : function(row, col, cm){
59091         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59092     },
59093     /**
59094      * Selects a cell.
59095      * @param {Number} field (not used) - as it's normally used as a listener
59096      * @param {Number} e - event - fake it by using
59097      *
59098      * var e = Roo.EventObjectImpl.prototype;
59099      * e.keyCode = e.TAB
59100      *
59101      * 
59102      */
59103     onEditorKey : function(field, e){
59104         
59105         var k = e.getKey(),
59106             newCell,
59107             g = this.grid,
59108             ed = g.activeEditor,
59109             forward = false;
59110         ///Roo.log('onEditorKey' + k);
59111         
59112         
59113         if (this.enter_is_tab && k == e.ENTER) {
59114             k = e.TAB;
59115         }
59116         
59117         if(k == e.TAB){
59118             if(e.shiftKey){
59119                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59120             }else{
59121                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59122                 forward = true;
59123             }
59124             
59125             e.stopEvent();
59126             
59127         } else if(k == e.ENTER &&  !e.ctrlKey){
59128             ed.completeEdit();
59129             e.stopEvent();
59130             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59131         
59132                 } else if(k == e.ESC){
59133             ed.cancelEdit();
59134         }
59135                 
59136         if (newCell) {
59137             var ecall = { cell : newCell, forward : forward };
59138             this.fireEvent('beforeeditnext', ecall );
59139             newCell = ecall.cell;
59140                         forward = ecall.forward;
59141         }
59142                 
59143         if(newCell){
59144             //Roo.log('next cell after edit');
59145             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59146         } else if (forward) {
59147             // tabbed past last
59148             this.fireEvent.defer(100, this, ['tabend',this]);
59149         }
59150     }
59151 });/*
59152  * Based on:
59153  * Ext JS Library 1.1.1
59154  * Copyright(c) 2006-2007, Ext JS, LLC.
59155  *
59156  * Originally Released Under LGPL - original licence link has changed is not relivant.
59157  *
59158  * Fork - LGPL
59159  * <script type="text/javascript">
59160  */
59161  
59162 /**
59163  * @class Roo.grid.EditorGrid
59164  * @extends Roo.grid.Grid
59165  * Class for creating and editable grid.
59166  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59167  * The container MUST have some type of size defined for the grid to fill. The container will be 
59168  * automatically set to position relative if it isn't already.
59169  * @param {Object} dataSource The data model to bind to
59170  * @param {Object} colModel The column model with info about this grid's columns
59171  */
59172 Roo.grid.EditorGrid = function(container, config){
59173     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59174     this.getGridEl().addClass("xedit-grid");
59175
59176     if(!this.selModel){
59177         this.selModel = new Roo.grid.CellSelectionModel();
59178     }
59179
59180     this.activeEditor = null;
59181
59182         this.addEvents({
59183             /**
59184              * @event beforeedit
59185              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59186              * <ul style="padding:5px;padding-left:16px;">
59187              * <li>grid - This grid</li>
59188              * <li>record - The record being edited</li>
59189              * <li>field - The field name being edited</li>
59190              * <li>value - The value for the field being edited.</li>
59191              * <li>row - The grid row index</li>
59192              * <li>column - The grid column index</li>
59193              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59194              * </ul>
59195              * @param {Object} e An edit event (see above for description)
59196              */
59197             "beforeedit" : true,
59198             /**
59199              * @event afteredit
59200              * Fires after a cell is edited. <br />
59201              * <ul style="padding:5px;padding-left:16px;">
59202              * <li>grid - This grid</li>
59203              * <li>record - The record being edited</li>
59204              * <li>field - The field name being edited</li>
59205              * <li>value - The value being set</li>
59206              * <li>originalValue - The original value for the field, before the edit.</li>
59207              * <li>row - The grid row index</li>
59208              * <li>column - The grid column index</li>
59209              * </ul>
59210              * @param {Object} e An edit event (see above for description)
59211              */
59212             "afteredit" : true,
59213             /**
59214              * @event validateedit
59215              * Fires after a cell is edited, but before the value is set in the record. 
59216          * You can use this to modify the value being set in the field, Return false
59217              * to cancel the change. The edit event object has the following properties <br />
59218              * <ul style="padding:5px;padding-left:16px;">
59219          * <li>editor - This editor</li>
59220              * <li>grid - This grid</li>
59221              * <li>record - The record being edited</li>
59222              * <li>field - The field name being edited</li>
59223              * <li>value - The value being set</li>
59224              * <li>originalValue - The original value for the field, before the edit.</li>
59225              * <li>row - The grid row index</li>
59226              * <li>column - The grid column index</li>
59227              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59228              * </ul>
59229              * @param {Object} e An edit event (see above for description)
59230              */
59231             "validateedit" : true
59232         });
59233     this.on("bodyscroll", this.stopEditing,  this);
59234     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59235 };
59236
59237 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59238     /**
59239      * @cfg {Number} clicksToEdit
59240      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59241      */
59242     clicksToEdit: 2,
59243
59244     // private
59245     isEditor : true,
59246     // private
59247     trackMouseOver: false, // causes very odd FF errors
59248
59249     onCellDblClick : function(g, row, col){
59250         this.startEditing(row, col);
59251     },
59252
59253     onEditComplete : function(ed, value, startValue){
59254         this.editing = false;
59255         this.activeEditor = null;
59256         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59257         var r = ed.record;
59258         var field = this.colModel.getDataIndex(ed.col);
59259         var e = {
59260             grid: this,
59261             record: r,
59262             field: field,
59263             originalValue: startValue,
59264             value: value,
59265             row: ed.row,
59266             column: ed.col,
59267             cancel:false,
59268             editor: ed
59269         };
59270         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59271         cell.show();
59272           
59273         if(String(value) !== String(startValue)){
59274             
59275             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59276                 r.set(field, e.value);
59277                 // if we are dealing with a combo box..
59278                 // then we also set the 'name' colum to be the displayField
59279                 if (ed.field.displayField && ed.field.name) {
59280                     r.set(ed.field.name, ed.field.el.dom.value);
59281                 }
59282                 
59283                 delete e.cancel; //?? why!!!
59284                 this.fireEvent("afteredit", e);
59285             }
59286         } else {
59287             this.fireEvent("afteredit", e); // always fire it!
59288         }
59289         this.view.focusCell(ed.row, ed.col);
59290     },
59291
59292     /**
59293      * Starts editing the specified for the specified row/column
59294      * @param {Number} rowIndex
59295      * @param {Number} colIndex
59296      */
59297     startEditing : function(row, col){
59298         this.stopEditing();
59299         if(this.colModel.isCellEditable(col, row)){
59300             this.view.ensureVisible(row, col, true);
59301           
59302             var r = this.dataSource.getAt(row);
59303             var field = this.colModel.getDataIndex(col);
59304             var cell = Roo.get(this.view.getCell(row,col));
59305             var e = {
59306                 grid: this,
59307                 record: r,
59308                 field: field,
59309                 value: r.data[field],
59310                 row: row,
59311                 column: col,
59312                 cancel:false 
59313             };
59314             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59315                 this.editing = true;
59316                 var ed = this.colModel.getCellEditor(col, row);
59317                 
59318                 if (!ed) {
59319                     return;
59320                 }
59321                 if(!ed.rendered){
59322                     ed.render(ed.parentEl || document.body);
59323                 }
59324                 ed.field.reset();
59325                
59326                 cell.hide();
59327                 
59328                 (function(){ // complex but required for focus issues in safari, ie and opera
59329                     ed.row = row;
59330                     ed.col = col;
59331                     ed.record = r;
59332                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59333                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
59334                     this.activeEditor = ed;
59335                     var v = r.data[field];
59336                     ed.startEdit(this.view.getCell(row, col), v);
59337                     // combo's with 'displayField and name set
59338                     if (ed.field.displayField && ed.field.name) {
59339                         ed.field.el.dom.value = r.data[ed.field.name];
59340                     }
59341                     
59342                     
59343                 }).defer(50, this);
59344             }
59345         }
59346     },
59347         
59348     /**
59349      * Stops any active editing
59350      */
59351     stopEditing : function(){
59352         if(this.activeEditor){
59353             this.activeEditor.completeEdit();
59354         }
59355         this.activeEditor = null;
59356     },
59357         
59358          /**
59359      * Called to get grid's drag proxy text, by default returns this.ddText.
59360      * @return {String}
59361      */
59362     getDragDropText : function(){
59363         var count = this.selModel.getSelectedCell() ? 1 : 0;
59364         return String.format(this.ddText, count, count == 1 ? '' : 's');
59365     }
59366         
59367 });/*
59368  * Based on:
59369  * Ext JS Library 1.1.1
59370  * Copyright(c) 2006-2007, Ext JS, LLC.
59371  *
59372  * Originally Released Under LGPL - original licence link has changed is not relivant.
59373  *
59374  * Fork - LGPL
59375  * <script type="text/javascript">
59376  */
59377
59378 // private - not really -- you end up using it !
59379 // This is a support class used internally by the Grid components
59380
59381 /**
59382  * @class Roo.grid.GridEditor
59383  * @extends Roo.Editor
59384  * Class for creating and editable grid elements.
59385  * @param {Object} config any settings (must include field)
59386  */
59387 Roo.grid.GridEditor = function(field, config){
59388     if (!config && field.field) {
59389         config = field;
59390         field = Roo.factory(config.field, Roo.form);
59391     }
59392     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59393     field.monitorTab = false;
59394 };
59395
59396 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59397     
59398     /**
59399      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59400      */
59401     
59402     alignment: "tl-tl",
59403     autoSize: "width",
59404     hideEl : false,
59405     cls: "x-small-editor x-grid-editor",
59406     shim:false,
59407     shadow:"frame"
59408 });/*
59409  * Based on:
59410  * Ext JS Library 1.1.1
59411  * Copyright(c) 2006-2007, Ext JS, LLC.
59412  *
59413  * Originally Released Under LGPL - original licence link has changed is not relivant.
59414  *
59415  * Fork - LGPL
59416  * <script type="text/javascript">
59417  */
59418   
59419
59420   
59421 Roo.grid.PropertyRecord = Roo.data.Record.create([
59422     {name:'name',type:'string'},  'value'
59423 ]);
59424
59425
59426 Roo.grid.PropertyStore = function(grid, source){
59427     this.grid = grid;
59428     this.store = new Roo.data.Store({
59429         recordType : Roo.grid.PropertyRecord
59430     });
59431     this.store.on('update', this.onUpdate,  this);
59432     if(source){
59433         this.setSource(source);
59434     }
59435     Roo.grid.PropertyStore.superclass.constructor.call(this);
59436 };
59437
59438
59439
59440 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
59441     setSource : function(o){
59442         this.source = o;
59443         this.store.removeAll();
59444         var data = [];
59445         for(var k in o){
59446             if(this.isEditableValue(o[k])){
59447                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
59448             }
59449         }
59450         this.store.loadRecords({records: data}, {}, true);
59451     },
59452
59453     onUpdate : function(ds, record, type){
59454         if(type == Roo.data.Record.EDIT){
59455             var v = record.data['value'];
59456             var oldValue = record.modified['value'];
59457             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
59458                 this.source[record.id] = v;
59459                 record.commit();
59460                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
59461             }else{
59462                 record.reject();
59463             }
59464         }
59465     },
59466
59467     getProperty : function(row){
59468        return this.store.getAt(row);
59469     },
59470
59471     isEditableValue: function(val){
59472         if(val && val instanceof Date){
59473             return true;
59474         }else if(typeof val == 'object' || typeof val == 'function'){
59475             return false;
59476         }
59477         return true;
59478     },
59479
59480     setValue : function(prop, value){
59481         this.source[prop] = value;
59482         this.store.getById(prop).set('value', value);
59483     },
59484
59485     getSource : function(){
59486         return this.source;
59487     }
59488 });
59489
59490 Roo.grid.PropertyColumnModel = function(grid, store){
59491     this.grid = grid;
59492     var g = Roo.grid;
59493     g.PropertyColumnModel.superclass.constructor.call(this, [
59494         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
59495         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
59496     ]);
59497     this.store = store;
59498     this.bselect = Roo.DomHelper.append(document.body, {
59499         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
59500             {tag: 'option', value: 'true', html: 'true'},
59501             {tag: 'option', value: 'false', html: 'false'}
59502         ]
59503     });
59504     Roo.id(this.bselect);
59505     var f = Roo.form;
59506     this.editors = {
59507         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
59508         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
59509         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
59510         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
59511         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
59512     };
59513     this.renderCellDelegate = this.renderCell.createDelegate(this);
59514     this.renderPropDelegate = this.renderProp.createDelegate(this);
59515 };
59516
59517 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
59518     
59519     
59520     nameText : 'Name',
59521     valueText : 'Value',
59522     
59523     dateFormat : 'm/j/Y',
59524     
59525     
59526     renderDate : function(dateVal){
59527         return dateVal.dateFormat(this.dateFormat);
59528     },
59529
59530     renderBool : function(bVal){
59531         return bVal ? 'true' : 'false';
59532     },
59533
59534     isCellEditable : function(colIndex, rowIndex){
59535         return colIndex == 1;
59536     },
59537
59538     getRenderer : function(col){
59539         return col == 1 ?
59540             this.renderCellDelegate : this.renderPropDelegate;
59541     },
59542
59543     renderProp : function(v){
59544         return this.getPropertyName(v);
59545     },
59546
59547     renderCell : function(val){
59548         var rv = val;
59549         if(val instanceof Date){
59550             rv = this.renderDate(val);
59551         }else if(typeof val == 'boolean'){
59552             rv = this.renderBool(val);
59553         }
59554         return Roo.util.Format.htmlEncode(rv);
59555     },
59556
59557     getPropertyName : function(name){
59558         var pn = this.grid.propertyNames;
59559         return pn && pn[name] ? pn[name] : name;
59560     },
59561
59562     getCellEditor : function(colIndex, rowIndex){
59563         var p = this.store.getProperty(rowIndex);
59564         var n = p.data['name'], val = p.data['value'];
59565         
59566         if(typeof(this.grid.customEditors[n]) == 'string'){
59567             return this.editors[this.grid.customEditors[n]];
59568         }
59569         if(typeof(this.grid.customEditors[n]) != 'undefined'){
59570             return this.grid.customEditors[n];
59571         }
59572         if(val instanceof Date){
59573             return this.editors['date'];
59574         }else if(typeof val == 'number'){
59575             return this.editors['number'];
59576         }else if(typeof val == 'boolean'){
59577             return this.editors['boolean'];
59578         }else{
59579             return this.editors['string'];
59580         }
59581     }
59582 });
59583
59584 /**
59585  * @class Roo.grid.PropertyGrid
59586  * @extends Roo.grid.EditorGrid
59587  * This class represents the  interface of a component based property grid control.
59588  * <br><br>Usage:<pre><code>
59589  var grid = new Roo.grid.PropertyGrid("my-container-id", {
59590       
59591  });
59592  // set any options
59593  grid.render();
59594  * </code></pre>
59595   
59596  * @constructor
59597  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59598  * The container MUST have some type of size defined for the grid to fill. The container will be
59599  * automatically set to position relative if it isn't already.
59600  * @param {Object} config A config object that sets properties on this grid.
59601  */
59602 Roo.grid.PropertyGrid = function(container, config){
59603     config = config || {};
59604     var store = new Roo.grid.PropertyStore(this);
59605     this.store = store;
59606     var cm = new Roo.grid.PropertyColumnModel(this, store);
59607     store.store.sort('name', 'ASC');
59608     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
59609         ds: store.store,
59610         cm: cm,
59611         enableColLock:false,
59612         enableColumnMove:false,
59613         stripeRows:false,
59614         trackMouseOver: false,
59615         clicksToEdit:1
59616     }, config));
59617     this.getGridEl().addClass('x-props-grid');
59618     this.lastEditRow = null;
59619     this.on('columnresize', this.onColumnResize, this);
59620     this.addEvents({
59621          /**
59622              * @event beforepropertychange
59623              * Fires before a property changes (return false to stop?)
59624              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59625              * @param {String} id Record Id
59626              * @param {String} newval New Value
59627          * @param {String} oldval Old Value
59628              */
59629         "beforepropertychange": true,
59630         /**
59631              * @event propertychange
59632              * Fires after a property changes
59633              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59634              * @param {String} id Record Id
59635              * @param {String} newval New Value
59636          * @param {String} oldval Old Value
59637              */
59638         "propertychange": true
59639     });
59640     this.customEditors = this.customEditors || {};
59641 };
59642 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
59643     
59644      /**
59645      * @cfg {Object} customEditors map of colnames=> custom editors.
59646      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
59647      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
59648      * false disables editing of the field.
59649          */
59650     
59651       /**
59652      * @cfg {Object} propertyNames map of property Names to their displayed value
59653          */
59654     
59655     render : function(){
59656         Roo.grid.PropertyGrid.superclass.render.call(this);
59657         this.autoSize.defer(100, this);
59658     },
59659
59660     autoSize : function(){
59661         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
59662         if(this.view){
59663             this.view.fitColumns();
59664         }
59665     },
59666
59667     onColumnResize : function(){
59668         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
59669         this.autoSize();
59670     },
59671     /**
59672      * Sets the data for the Grid
59673      * accepts a Key => Value object of all the elements avaiable.
59674      * @param {Object} data  to appear in grid.
59675      */
59676     setSource : function(source){
59677         this.store.setSource(source);
59678         //this.autoSize();
59679     },
59680     /**
59681      * Gets all the data from the grid.
59682      * @return {Object} data  data stored in grid
59683      */
59684     getSource : function(){
59685         return this.store.getSource();
59686     }
59687 });/*
59688   
59689  * Licence LGPL
59690  
59691  */
59692  
59693 /**
59694  * @class Roo.grid.Calendar
59695  * @extends Roo.util.Grid
59696  * This class extends the Grid to provide a calendar widget
59697  * <br><br>Usage:<pre><code>
59698  var grid = new Roo.grid.Calendar("my-container-id", {
59699      ds: myDataStore,
59700      cm: myColModel,
59701      selModel: mySelectionModel,
59702      autoSizeColumns: true,
59703      monitorWindowResize: false,
59704      trackMouseOver: true
59705      eventstore : real data store..
59706  });
59707  // set any options
59708  grid.render();
59709   
59710   * @constructor
59711  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59712  * The container MUST have some type of size defined for the grid to fill. The container will be
59713  * automatically set to position relative if it isn't already.
59714  * @param {Object} config A config object that sets properties on this grid.
59715  */
59716 Roo.grid.Calendar = function(container, config){
59717         // initialize the container
59718         this.container = Roo.get(container);
59719         this.container.update("");
59720         this.container.setStyle("overflow", "hidden");
59721     this.container.addClass('x-grid-container');
59722
59723     this.id = this.container.id;
59724
59725     Roo.apply(this, config);
59726     // check and correct shorthanded configs
59727     
59728     var rows = [];
59729     var d =1;
59730     for (var r = 0;r < 6;r++) {
59731         
59732         rows[r]=[];
59733         for (var c =0;c < 7;c++) {
59734             rows[r][c]= '';
59735         }
59736     }
59737     if (this.eventStore) {
59738         this.eventStore= Roo.factory(this.eventStore, Roo.data);
59739         this.eventStore.on('load',this.onLoad, this);
59740         this.eventStore.on('beforeload',this.clearEvents, this);
59741          
59742     }
59743     
59744     this.dataSource = new Roo.data.Store({
59745             proxy: new Roo.data.MemoryProxy(rows),
59746             reader: new Roo.data.ArrayReader({}, [
59747                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
59748     });
59749
59750     this.dataSource.load();
59751     this.ds = this.dataSource;
59752     this.ds.xmodule = this.xmodule || false;
59753     
59754     
59755     var cellRender = function(v,x,r)
59756     {
59757         return String.format(
59758             '<div class="fc-day  fc-widget-content"><div>' +
59759                 '<div class="fc-event-container"></div>' +
59760                 '<div class="fc-day-number">{0}</div>'+
59761                 
59762                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
59763             '</div></div>', v);
59764     
59765     }
59766     
59767     
59768     this.colModel = new Roo.grid.ColumnModel( [
59769         {
59770             xtype: 'ColumnModel',
59771             xns: Roo.grid,
59772             dataIndex : 'weekday0',
59773             header : 'Sunday',
59774             renderer : cellRender
59775         },
59776         {
59777             xtype: 'ColumnModel',
59778             xns: Roo.grid,
59779             dataIndex : 'weekday1',
59780             header : 'Monday',
59781             renderer : cellRender
59782         },
59783         {
59784             xtype: 'ColumnModel',
59785             xns: Roo.grid,
59786             dataIndex : 'weekday2',
59787             header : 'Tuesday',
59788             renderer : cellRender
59789         },
59790         {
59791             xtype: 'ColumnModel',
59792             xns: Roo.grid,
59793             dataIndex : 'weekday3',
59794             header : 'Wednesday',
59795             renderer : cellRender
59796         },
59797         {
59798             xtype: 'ColumnModel',
59799             xns: Roo.grid,
59800             dataIndex : 'weekday4',
59801             header : 'Thursday',
59802             renderer : cellRender
59803         },
59804         {
59805             xtype: 'ColumnModel',
59806             xns: Roo.grid,
59807             dataIndex : 'weekday5',
59808             header : 'Friday',
59809             renderer : cellRender
59810         },
59811         {
59812             xtype: 'ColumnModel',
59813             xns: Roo.grid,
59814             dataIndex : 'weekday6',
59815             header : 'Saturday',
59816             renderer : cellRender
59817         }
59818     ]);
59819     this.cm = this.colModel;
59820     this.cm.xmodule = this.xmodule || false;
59821  
59822         
59823           
59824     //this.selModel = new Roo.grid.CellSelectionModel();
59825     //this.sm = this.selModel;
59826     //this.selModel.init(this);
59827     
59828     
59829     if(this.width){
59830         this.container.setWidth(this.width);
59831     }
59832
59833     if(this.height){
59834         this.container.setHeight(this.height);
59835     }
59836     /** @private */
59837         this.addEvents({
59838         // raw events
59839         /**
59840          * @event click
59841          * The raw click event for the entire grid.
59842          * @param {Roo.EventObject} e
59843          */
59844         "click" : true,
59845         /**
59846          * @event dblclick
59847          * The raw dblclick event for the entire grid.
59848          * @param {Roo.EventObject} e
59849          */
59850         "dblclick" : true,
59851         /**
59852          * @event contextmenu
59853          * The raw contextmenu event for the entire grid.
59854          * @param {Roo.EventObject} e
59855          */
59856         "contextmenu" : true,
59857         /**
59858          * @event mousedown
59859          * The raw mousedown event for the entire grid.
59860          * @param {Roo.EventObject} e
59861          */
59862         "mousedown" : true,
59863         /**
59864          * @event mouseup
59865          * The raw mouseup event for the entire grid.
59866          * @param {Roo.EventObject} e
59867          */
59868         "mouseup" : true,
59869         /**
59870          * @event mouseover
59871          * The raw mouseover event for the entire grid.
59872          * @param {Roo.EventObject} e
59873          */
59874         "mouseover" : true,
59875         /**
59876          * @event mouseout
59877          * The raw mouseout event for the entire grid.
59878          * @param {Roo.EventObject} e
59879          */
59880         "mouseout" : true,
59881         /**
59882          * @event keypress
59883          * The raw keypress event for the entire grid.
59884          * @param {Roo.EventObject} e
59885          */
59886         "keypress" : true,
59887         /**
59888          * @event keydown
59889          * The raw keydown event for the entire grid.
59890          * @param {Roo.EventObject} e
59891          */
59892         "keydown" : true,
59893
59894         // custom events
59895
59896         /**
59897          * @event cellclick
59898          * Fires when a cell is clicked
59899          * @param {Grid} this
59900          * @param {Number} rowIndex
59901          * @param {Number} columnIndex
59902          * @param {Roo.EventObject} e
59903          */
59904         "cellclick" : true,
59905         /**
59906          * @event celldblclick
59907          * Fires when a cell is double clicked
59908          * @param {Grid} this
59909          * @param {Number} rowIndex
59910          * @param {Number} columnIndex
59911          * @param {Roo.EventObject} e
59912          */
59913         "celldblclick" : true,
59914         /**
59915          * @event rowclick
59916          * Fires when a row is clicked
59917          * @param {Grid} this
59918          * @param {Number} rowIndex
59919          * @param {Roo.EventObject} e
59920          */
59921         "rowclick" : true,
59922         /**
59923          * @event rowdblclick
59924          * Fires when a row is double clicked
59925          * @param {Grid} this
59926          * @param {Number} rowIndex
59927          * @param {Roo.EventObject} e
59928          */
59929         "rowdblclick" : true,
59930         /**
59931          * @event headerclick
59932          * Fires when a header is clicked
59933          * @param {Grid} this
59934          * @param {Number} columnIndex
59935          * @param {Roo.EventObject} e
59936          */
59937         "headerclick" : true,
59938         /**
59939          * @event headerdblclick
59940          * Fires when a header cell is double clicked
59941          * @param {Grid} this
59942          * @param {Number} columnIndex
59943          * @param {Roo.EventObject} e
59944          */
59945         "headerdblclick" : true,
59946         /**
59947          * @event rowcontextmenu
59948          * Fires when a row is right clicked
59949          * @param {Grid} this
59950          * @param {Number} rowIndex
59951          * @param {Roo.EventObject} e
59952          */
59953         "rowcontextmenu" : true,
59954         /**
59955          * @event cellcontextmenu
59956          * Fires when a cell is right clicked
59957          * @param {Grid} this
59958          * @param {Number} rowIndex
59959          * @param {Number} cellIndex
59960          * @param {Roo.EventObject} e
59961          */
59962          "cellcontextmenu" : true,
59963         /**
59964          * @event headercontextmenu
59965          * Fires when a header is right clicked
59966          * @param {Grid} this
59967          * @param {Number} columnIndex
59968          * @param {Roo.EventObject} e
59969          */
59970         "headercontextmenu" : true,
59971         /**
59972          * @event bodyscroll
59973          * Fires when the body element is scrolled
59974          * @param {Number} scrollLeft
59975          * @param {Number} scrollTop
59976          */
59977         "bodyscroll" : true,
59978         /**
59979          * @event columnresize
59980          * Fires when the user resizes a column
59981          * @param {Number} columnIndex
59982          * @param {Number} newSize
59983          */
59984         "columnresize" : true,
59985         /**
59986          * @event columnmove
59987          * Fires when the user moves a column
59988          * @param {Number} oldIndex
59989          * @param {Number} newIndex
59990          */
59991         "columnmove" : true,
59992         /**
59993          * @event startdrag
59994          * Fires when row(s) start being dragged
59995          * @param {Grid} this
59996          * @param {Roo.GridDD} dd The drag drop object
59997          * @param {event} e The raw browser event
59998          */
59999         "startdrag" : true,
60000         /**
60001          * @event enddrag
60002          * Fires when a drag operation is complete
60003          * @param {Grid} this
60004          * @param {Roo.GridDD} dd The drag drop object
60005          * @param {event} e The raw browser event
60006          */
60007         "enddrag" : true,
60008         /**
60009          * @event dragdrop
60010          * Fires when dragged row(s) are dropped on a valid DD target
60011          * @param {Grid} this
60012          * @param {Roo.GridDD} dd The drag drop object
60013          * @param {String} targetId The target drag drop object
60014          * @param {event} e The raw browser event
60015          */
60016         "dragdrop" : true,
60017         /**
60018          * @event dragover
60019          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60020          * @param {Grid} this
60021          * @param {Roo.GridDD} dd The drag drop object
60022          * @param {String} targetId The target drag drop object
60023          * @param {event} e The raw browser event
60024          */
60025         "dragover" : true,
60026         /**
60027          * @event dragenter
60028          *  Fires when the dragged row(s) first cross another DD target while being dragged
60029          * @param {Grid} this
60030          * @param {Roo.GridDD} dd The drag drop object
60031          * @param {String} targetId The target drag drop object
60032          * @param {event} e The raw browser event
60033          */
60034         "dragenter" : true,
60035         /**
60036          * @event dragout
60037          * Fires when the dragged row(s) leave another DD target while being dragged
60038          * @param {Grid} this
60039          * @param {Roo.GridDD} dd The drag drop object
60040          * @param {String} targetId The target drag drop object
60041          * @param {event} e The raw browser event
60042          */
60043         "dragout" : true,
60044         /**
60045          * @event rowclass
60046          * Fires when a row is rendered, so you can change add a style to it.
60047          * @param {GridView} gridview   The grid view
60048          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60049          */
60050         'rowclass' : true,
60051
60052         /**
60053          * @event render
60054          * Fires when the grid is rendered
60055          * @param {Grid} grid
60056          */
60057         'render' : true,
60058             /**
60059              * @event select
60060              * Fires when a date is selected
60061              * @param {DatePicker} this
60062              * @param {Date} date The selected date
60063              */
60064         'select': true,
60065         /**
60066              * @event monthchange
60067              * Fires when the displayed month changes 
60068              * @param {DatePicker} this
60069              * @param {Date} date The selected month
60070              */
60071         'monthchange': true,
60072         /**
60073              * @event evententer
60074              * Fires when mouse over an event
60075              * @param {Calendar} this
60076              * @param {event} Event
60077              */
60078         'evententer': true,
60079         /**
60080              * @event eventleave
60081              * Fires when the mouse leaves an
60082              * @param {Calendar} this
60083              * @param {event}
60084              */
60085         'eventleave': true,
60086         /**
60087              * @event eventclick
60088              * Fires when the mouse click an
60089              * @param {Calendar} this
60090              * @param {event}
60091              */
60092         'eventclick': true,
60093         /**
60094              * @event eventrender
60095              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60096              * @param {Calendar} this
60097              * @param {data} data to be modified
60098              */
60099         'eventrender': true
60100         
60101     });
60102
60103     Roo.grid.Grid.superclass.constructor.call(this);
60104     this.on('render', function() {
60105         this.view.el.addClass('x-grid-cal'); 
60106         
60107         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60108
60109     },this);
60110     
60111     if (!Roo.grid.Calendar.style) {
60112         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60113             
60114             
60115             '.x-grid-cal .x-grid-col' :  {
60116                 height: 'auto !important',
60117                 'vertical-align': 'top'
60118             },
60119             '.x-grid-cal  .fc-event-hori' : {
60120                 height: '14px'
60121             }
60122              
60123             
60124         }, Roo.id());
60125     }
60126
60127     
60128     
60129 };
60130 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60131     /**
60132      * @cfg {Store} eventStore The store that loads events.
60133      */
60134     eventStore : 25,
60135
60136      
60137     activeDate : false,
60138     startDay : 0,
60139     autoWidth : true,
60140     monitorWindowResize : false,
60141
60142     
60143     resizeColumns : function() {
60144         var col = (this.view.el.getWidth() / 7) - 3;
60145         // loop through cols, and setWidth
60146         for(var i =0 ; i < 7 ; i++){
60147             this.cm.setColumnWidth(i, col);
60148         }
60149     },
60150      setDate :function(date) {
60151         
60152         Roo.log('setDate?');
60153         
60154         this.resizeColumns();
60155         var vd = this.activeDate;
60156         this.activeDate = date;
60157 //        if(vd && this.el){
60158 //            var t = date.getTime();
60159 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60160 //                Roo.log('using add remove');
60161 //                
60162 //                this.fireEvent('monthchange', this, date);
60163 //                
60164 //                this.cells.removeClass("fc-state-highlight");
60165 //                this.cells.each(function(c){
60166 //                   if(c.dateValue == t){
60167 //                       c.addClass("fc-state-highlight");
60168 //                       setTimeout(function(){
60169 //                            try{c.dom.firstChild.focus();}catch(e){}
60170 //                       }, 50);
60171 //                       return false;
60172 //                   }
60173 //                   return true;
60174 //                });
60175 //                return;
60176 //            }
60177 //        }
60178         
60179         var days = date.getDaysInMonth();
60180         
60181         var firstOfMonth = date.getFirstDateOfMonth();
60182         var startingPos = firstOfMonth.getDay()-this.startDay;
60183         
60184         if(startingPos < this.startDay){
60185             startingPos += 7;
60186         }
60187         
60188         var pm = date.add(Date.MONTH, -1);
60189         var prevStart = pm.getDaysInMonth()-startingPos;
60190 //        
60191         
60192         
60193         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60194         
60195         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60196         //this.cells.addClassOnOver('fc-state-hover');
60197         
60198         var cells = this.cells.elements;
60199         var textEls = this.textNodes;
60200         
60201         //Roo.each(cells, function(cell){
60202         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60203         //});
60204         
60205         days += startingPos;
60206
60207         // convert everything to numbers so it's fast
60208         var day = 86400000;
60209         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60210         //Roo.log(d);
60211         //Roo.log(pm);
60212         //Roo.log(prevStart);
60213         
60214         var today = new Date().clearTime().getTime();
60215         var sel = date.clearTime().getTime();
60216         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60217         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60218         var ddMatch = this.disabledDatesRE;
60219         var ddText = this.disabledDatesText;
60220         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60221         var ddaysText = this.disabledDaysText;
60222         var format = this.format;
60223         
60224         var setCellClass = function(cal, cell){
60225             
60226             //Roo.log('set Cell Class');
60227             cell.title = "";
60228             var t = d.getTime();
60229             
60230             //Roo.log(d);
60231             
60232             
60233             cell.dateValue = t;
60234             if(t == today){
60235                 cell.className += " fc-today";
60236                 cell.className += " fc-state-highlight";
60237                 cell.title = cal.todayText;
60238             }
60239             if(t == sel){
60240                 // disable highlight in other month..
60241                 cell.className += " fc-state-highlight";
60242                 
60243             }
60244             // disabling
60245             if(t < min) {
60246                 //cell.className = " fc-state-disabled";
60247                 cell.title = cal.minText;
60248                 return;
60249             }
60250             if(t > max) {
60251                 //cell.className = " fc-state-disabled";
60252                 cell.title = cal.maxText;
60253                 return;
60254             }
60255             if(ddays){
60256                 if(ddays.indexOf(d.getDay()) != -1){
60257                     // cell.title = ddaysText;
60258                    // cell.className = " fc-state-disabled";
60259                 }
60260             }
60261             if(ddMatch && format){
60262                 var fvalue = d.dateFormat(format);
60263                 if(ddMatch.test(fvalue)){
60264                     cell.title = ddText.replace("%0", fvalue);
60265                    cell.className = " fc-state-disabled";
60266                 }
60267             }
60268             
60269             if (!cell.initialClassName) {
60270                 cell.initialClassName = cell.dom.className;
60271             }
60272             
60273             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60274         };
60275
60276         var i = 0;
60277         
60278         for(; i < startingPos; i++) {
60279             cells[i].dayName =  (++prevStart);
60280             Roo.log(textEls[i]);
60281             d.setDate(d.getDate()+1);
60282             
60283             //cells[i].className = "fc-past fc-other-month";
60284             setCellClass(this, cells[i]);
60285         }
60286         
60287         var intDay = 0;
60288         
60289         for(; i < days; i++){
60290             intDay = i - startingPos + 1;
60291             cells[i].dayName =  (intDay);
60292             d.setDate(d.getDate()+1);
60293             
60294             cells[i].className = ''; // "x-date-active";
60295             setCellClass(this, cells[i]);
60296         }
60297         var extraDays = 0;
60298         
60299         for(; i < 42; i++) {
60300             //textEls[i].innerHTML = (++extraDays);
60301             
60302             d.setDate(d.getDate()+1);
60303             cells[i].dayName = (++extraDays);
60304             cells[i].className = "fc-future fc-other-month";
60305             setCellClass(this, cells[i]);
60306         }
60307         
60308         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60309         
60310         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60311         
60312         // this will cause all the cells to mis
60313         var rows= [];
60314         var i =0;
60315         for (var r = 0;r < 6;r++) {
60316             for (var c =0;c < 7;c++) {
60317                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60318             }    
60319         }
60320         
60321         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60322         for(i=0;i<cells.length;i++) {
60323             
60324             this.cells.elements[i].dayName = cells[i].dayName ;
60325             this.cells.elements[i].className = cells[i].className;
60326             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60327             this.cells.elements[i].title = cells[i].title ;
60328             this.cells.elements[i].dateValue = cells[i].dateValue ;
60329         }
60330         
60331         
60332         
60333         
60334         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
60335         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
60336         
60337         ////if(totalRows != 6){
60338             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
60339            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
60340        // }
60341         
60342         this.fireEvent('monthchange', this, date);
60343         
60344         
60345     },
60346  /**
60347      * Returns the grid's SelectionModel.
60348      * @return {SelectionModel}
60349      */
60350     getSelectionModel : function(){
60351         if(!this.selModel){
60352             this.selModel = new Roo.grid.CellSelectionModel();
60353         }
60354         return this.selModel;
60355     },
60356
60357     load: function() {
60358         this.eventStore.load()
60359         
60360         
60361         
60362     },
60363     
60364     findCell : function(dt) {
60365         dt = dt.clearTime().getTime();
60366         var ret = false;
60367         this.cells.each(function(c){
60368             //Roo.log("check " +c.dateValue + '?=' + dt);
60369             if(c.dateValue == dt){
60370                 ret = c;
60371                 return false;
60372             }
60373             return true;
60374         });
60375         
60376         return ret;
60377     },
60378     
60379     findCells : function(rec) {
60380         var s = rec.data.start_dt.clone().clearTime().getTime();
60381        // Roo.log(s);
60382         var e= rec.data.end_dt.clone().clearTime().getTime();
60383        // Roo.log(e);
60384         var ret = [];
60385         this.cells.each(function(c){
60386              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60387             
60388             if(c.dateValue > e){
60389                 return ;
60390             }
60391             if(c.dateValue < s){
60392                 return ;
60393             }
60394             ret.push(c);
60395         });
60396         
60397         return ret;    
60398     },
60399     
60400     findBestRow: function(cells)
60401     {
60402         var ret = 0;
60403         
60404         for (var i =0 ; i < cells.length;i++) {
60405             ret  = Math.max(cells[i].rows || 0,ret);
60406         }
60407         return ret;
60408         
60409     },
60410     
60411     
60412     addItem : function(rec)
60413     {
60414         // look for vertical location slot in
60415         var cells = this.findCells(rec);
60416         
60417         rec.row = this.findBestRow(cells);
60418         
60419         // work out the location.
60420         
60421         var crow = false;
60422         var rows = [];
60423         for(var i =0; i < cells.length; i++) {
60424             if (!crow) {
60425                 crow = {
60426                     start : cells[i],
60427                     end :  cells[i]
60428                 };
60429                 continue;
60430             }
60431             if (crow.start.getY() == cells[i].getY()) {
60432                 // on same row.
60433                 crow.end = cells[i];
60434                 continue;
60435             }
60436             // different row.
60437             rows.push(crow);
60438             crow = {
60439                 start: cells[i],
60440                 end : cells[i]
60441             };
60442             
60443         }
60444         
60445         rows.push(crow);
60446         rec.els = [];
60447         rec.rows = rows;
60448         rec.cells = cells;
60449         for (var i = 0; i < cells.length;i++) {
60450             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
60451             
60452         }
60453         
60454         
60455     },
60456     
60457     clearEvents: function() {
60458         
60459         if (!this.eventStore.getCount()) {
60460             return;
60461         }
60462         // reset number of rows in cells.
60463         Roo.each(this.cells.elements, function(c){
60464             c.rows = 0;
60465         });
60466         
60467         this.eventStore.each(function(e) {
60468             this.clearEvent(e);
60469         },this);
60470         
60471     },
60472     
60473     clearEvent : function(ev)
60474     {
60475         if (ev.els) {
60476             Roo.each(ev.els, function(el) {
60477                 el.un('mouseenter' ,this.onEventEnter, this);
60478                 el.un('mouseleave' ,this.onEventLeave, this);
60479                 el.remove();
60480             },this);
60481             ev.els = [];
60482         }
60483     },
60484     
60485     
60486     renderEvent : function(ev,ctr) {
60487         if (!ctr) {
60488              ctr = this.view.el.select('.fc-event-container',true).first();
60489         }
60490         
60491          
60492         this.clearEvent(ev);
60493             //code
60494        
60495         
60496         
60497         ev.els = [];
60498         var cells = ev.cells;
60499         var rows = ev.rows;
60500         this.fireEvent('eventrender', this, ev);
60501         
60502         for(var i =0; i < rows.length; i++) {
60503             
60504             cls = '';
60505             if (i == 0) {
60506                 cls += ' fc-event-start';
60507             }
60508             if ((i+1) == rows.length) {
60509                 cls += ' fc-event-end';
60510             }
60511             
60512             //Roo.log(ev.data);
60513             // how many rows should it span..
60514             var cg = this.eventTmpl.append(ctr,Roo.apply({
60515                 fccls : cls
60516                 
60517             }, ev.data) , true);
60518             
60519             
60520             cg.on('mouseenter' ,this.onEventEnter, this, ev);
60521             cg.on('mouseleave' ,this.onEventLeave, this, ev);
60522             cg.on('click', this.onEventClick, this, ev);
60523             
60524             ev.els.push(cg);
60525             
60526             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
60527             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
60528             //Roo.log(cg);
60529              
60530             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
60531             cg.setWidth(ebox.right - sbox.x -2);
60532         }
60533     },
60534     
60535     renderEvents: function()
60536     {   
60537         // first make sure there is enough space..
60538         
60539         if (!this.eventTmpl) {
60540             this.eventTmpl = new Roo.Template(
60541                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
60542                     '<div class="fc-event-inner">' +
60543                         '<span class="fc-event-time">{time}</span>' +
60544                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
60545                     '</div>' +
60546                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
60547                 '</div>'
60548             );
60549                 
60550         }
60551                
60552         
60553         
60554         this.cells.each(function(c) {
60555             //Roo.log(c.select('.fc-day-content div',true).first());
60556             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
60557         });
60558         
60559         var ctr = this.view.el.select('.fc-event-container',true).first();
60560         
60561         var cls;
60562         this.eventStore.each(function(ev){
60563             
60564             this.renderEvent(ev);
60565              
60566              
60567         }, this);
60568         this.view.layout();
60569         
60570     },
60571     
60572     onEventEnter: function (e, el,event,d) {
60573         this.fireEvent('evententer', this, el, event);
60574     },
60575     
60576     onEventLeave: function (e, el,event,d) {
60577         this.fireEvent('eventleave', this, el, event);
60578     },
60579     
60580     onEventClick: function (e, el,event,d) {
60581         this.fireEvent('eventclick', this, el, event);
60582     },
60583     
60584     onMonthChange: function () {
60585         this.store.load();
60586     },
60587     
60588     onLoad: function () {
60589         
60590         //Roo.log('calendar onload');
60591 //         
60592         if(this.eventStore.getCount() > 0){
60593             
60594            
60595             
60596             this.eventStore.each(function(d){
60597                 
60598                 
60599                 // FIXME..
60600                 var add =   d.data;
60601                 if (typeof(add.end_dt) == 'undefined')  {
60602                     Roo.log("Missing End time in calendar data: ");
60603                     Roo.log(d);
60604                     return;
60605                 }
60606                 if (typeof(add.start_dt) == 'undefined')  {
60607                     Roo.log("Missing Start time in calendar data: ");
60608                     Roo.log(d);
60609                     return;
60610                 }
60611                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
60612                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
60613                 add.id = add.id || d.id;
60614                 add.title = add.title || '??';
60615                 
60616                 this.addItem(d);
60617                 
60618              
60619             },this);
60620         }
60621         
60622         this.renderEvents();
60623     }
60624     
60625
60626 });
60627 /*
60628  grid : {
60629                 xtype: 'Grid',
60630                 xns: Roo.grid,
60631                 listeners : {
60632                     render : function ()
60633                     {
60634                         _this.grid = this;
60635                         
60636                         if (!this.view.el.hasClass('course-timesheet')) {
60637                             this.view.el.addClass('course-timesheet');
60638                         }
60639                         if (this.tsStyle) {
60640                             this.ds.load({});
60641                             return; 
60642                         }
60643                         Roo.log('width');
60644                         Roo.log(_this.grid.view.el.getWidth());
60645                         
60646                         
60647                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
60648                             '.course-timesheet .x-grid-row' : {
60649                                 height: '80px'
60650                             },
60651                             '.x-grid-row td' : {
60652                                 'vertical-align' : 0
60653                             },
60654                             '.course-edit-link' : {
60655                                 'color' : 'blue',
60656                                 'text-overflow' : 'ellipsis',
60657                                 'overflow' : 'hidden',
60658                                 'white-space' : 'nowrap',
60659                                 'cursor' : 'pointer'
60660                             },
60661                             '.sub-link' : {
60662                                 'color' : 'green'
60663                             },
60664                             '.de-act-sup-link' : {
60665                                 'color' : 'purple',
60666                                 'text-decoration' : 'line-through'
60667                             },
60668                             '.de-act-link' : {
60669                                 'color' : 'red',
60670                                 'text-decoration' : 'line-through'
60671                             },
60672                             '.course-timesheet .course-highlight' : {
60673                                 'border-top-style': 'dashed !important',
60674                                 'border-bottom-bottom': 'dashed !important'
60675                             },
60676                             '.course-timesheet .course-item' : {
60677                                 'font-family'   : 'tahoma, arial, helvetica',
60678                                 'font-size'     : '11px',
60679                                 'overflow'      : 'hidden',
60680                                 'padding-left'  : '10px',
60681                                 'padding-right' : '10px',
60682                                 'padding-top' : '10px' 
60683                             }
60684                             
60685                         }, Roo.id());
60686                                 this.ds.load({});
60687                     }
60688                 },
60689                 autoWidth : true,
60690                 monitorWindowResize : false,
60691                 cellrenderer : function(v,x,r)
60692                 {
60693                     return v;
60694                 },
60695                 sm : {
60696                     xtype: 'CellSelectionModel',
60697                     xns: Roo.grid
60698                 },
60699                 dataSource : {
60700                     xtype: 'Store',
60701                     xns: Roo.data,
60702                     listeners : {
60703                         beforeload : function (_self, options)
60704                         {
60705                             options.params = options.params || {};
60706                             options.params._month = _this.monthField.getValue();
60707                             options.params.limit = 9999;
60708                             options.params['sort'] = 'when_dt';    
60709                             options.params['dir'] = 'ASC';    
60710                             this.proxy.loadResponse = this.loadResponse;
60711                             Roo.log("load?");
60712                             //this.addColumns();
60713                         },
60714                         load : function (_self, records, options)
60715                         {
60716                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
60717                                 // if you click on the translation.. you can edit it...
60718                                 var el = Roo.get(this);
60719                                 var id = el.dom.getAttribute('data-id');
60720                                 var d = el.dom.getAttribute('data-date');
60721                                 var t = el.dom.getAttribute('data-time');
60722                                 //var id = this.child('span').dom.textContent;
60723                                 
60724                                 //Roo.log(this);
60725                                 Pman.Dialog.CourseCalendar.show({
60726                                     id : id,
60727                                     when_d : d,
60728                                     when_t : t,
60729                                     productitem_active : id ? 1 : 0
60730                                 }, function() {
60731                                     _this.grid.ds.load({});
60732                                 });
60733                            
60734                            });
60735                            
60736                            _this.panel.fireEvent('resize', [ '', '' ]);
60737                         }
60738                     },
60739                     loadResponse : function(o, success, response){
60740                             // this is overridden on before load..
60741                             
60742                             Roo.log("our code?");       
60743                             //Roo.log(success);
60744                             //Roo.log(response)
60745                             delete this.activeRequest;
60746                             if(!success){
60747                                 this.fireEvent("loadexception", this, o, response);
60748                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60749                                 return;
60750                             }
60751                             var result;
60752                             try {
60753                                 result = o.reader.read(response);
60754                             }catch(e){
60755                                 Roo.log("load exception?");
60756                                 this.fireEvent("loadexception", this, o, response, e);
60757                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60758                                 return;
60759                             }
60760                             Roo.log("ready...");        
60761                             // loop through result.records;
60762                             // and set this.tdate[date] = [] << array of records..
60763                             _this.tdata  = {};
60764                             Roo.each(result.records, function(r){
60765                                 //Roo.log(r.data);
60766                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
60767                                     _this.tdata[r.data.when_dt.format('j')] = [];
60768                                 }
60769                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
60770                             });
60771                             
60772                             //Roo.log(_this.tdata);
60773                             
60774                             result.records = [];
60775                             result.totalRecords = 6;
60776                     
60777                             // let's generate some duumy records for the rows.
60778                             //var st = _this.dateField.getValue();
60779                             
60780                             // work out monday..
60781                             //st = st.add(Date.DAY, -1 * st.format('w'));
60782                             
60783                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60784                             
60785                             var firstOfMonth = date.getFirstDayOfMonth();
60786                             var days = date.getDaysInMonth();
60787                             var d = 1;
60788                             var firstAdded = false;
60789                             for (var i = 0; i < result.totalRecords ; i++) {
60790                                 //var d= st.add(Date.DAY, i);
60791                                 var row = {};
60792                                 var added = 0;
60793                                 for(var w = 0 ; w < 7 ; w++){
60794                                     if(!firstAdded && firstOfMonth != w){
60795                                         continue;
60796                                     }
60797                                     if(d > days){
60798                                         continue;
60799                                     }
60800                                     firstAdded = true;
60801                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
60802                                     row['weekday'+w] = String.format(
60803                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
60804                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
60805                                                     d,
60806                                                     date.format('Y-m-')+dd
60807                                                 );
60808                                     added++;
60809                                     if(typeof(_this.tdata[d]) != 'undefined'){
60810                                         Roo.each(_this.tdata[d], function(r){
60811                                             var is_sub = '';
60812                                             var deactive = '';
60813                                             var id = r.id;
60814                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
60815                                             if(r.parent_id*1>0){
60816                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
60817                                                 id = r.parent_id;
60818                                             }
60819                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
60820                                                 deactive = 'de-act-link';
60821                                             }
60822                                             
60823                                             row['weekday'+w] += String.format(
60824                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
60825                                                     id, //0
60826                                                     r.product_id_name, //1
60827                                                     r.when_dt.format('h:ia'), //2
60828                                                     is_sub, //3
60829                                                     deactive, //4
60830                                                     desc // 5
60831                                             );
60832                                         });
60833                                     }
60834                                     d++;
60835                                 }
60836                                 
60837                                 // only do this if something added..
60838                                 if(added > 0){ 
60839                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
60840                                 }
60841                                 
60842                                 
60843                                 // push it twice. (second one with an hour..
60844                                 
60845                             }
60846                             //Roo.log(result);
60847                             this.fireEvent("load", this, o, o.request.arg);
60848                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
60849                         },
60850                     sortInfo : {field: 'when_dt', direction : 'ASC' },
60851                     proxy : {
60852                         xtype: 'HttpProxy',
60853                         xns: Roo.data,
60854                         method : 'GET',
60855                         url : baseURL + '/Roo/Shop_course.php'
60856                     },
60857                     reader : {
60858                         xtype: 'JsonReader',
60859                         xns: Roo.data,
60860                         id : 'id',
60861                         fields : [
60862                             {
60863                                 'name': 'id',
60864                                 'type': 'int'
60865                             },
60866                             {
60867                                 'name': 'when_dt',
60868                                 'type': 'string'
60869                             },
60870                             {
60871                                 'name': 'end_dt',
60872                                 'type': 'string'
60873                             },
60874                             {
60875                                 'name': 'parent_id',
60876                                 'type': 'int'
60877                             },
60878                             {
60879                                 'name': 'product_id',
60880                                 'type': 'int'
60881                             },
60882                             {
60883                                 'name': 'productitem_id',
60884                                 'type': 'int'
60885                             },
60886                             {
60887                                 'name': 'guid',
60888                                 'type': 'int'
60889                             }
60890                         ]
60891                     }
60892                 },
60893                 toolbar : {
60894                     xtype: 'Toolbar',
60895                     xns: Roo,
60896                     items : [
60897                         {
60898                             xtype: 'Button',
60899                             xns: Roo.Toolbar,
60900                             listeners : {
60901                                 click : function (_self, e)
60902                                 {
60903                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60904                                     sd.setMonth(sd.getMonth()-1);
60905                                     _this.monthField.setValue(sd.format('Y-m-d'));
60906                                     _this.grid.ds.load({});
60907                                 }
60908                             },
60909                             text : "Back"
60910                         },
60911                         {
60912                             xtype: 'Separator',
60913                             xns: Roo.Toolbar
60914                         },
60915                         {
60916                             xtype: 'MonthField',
60917                             xns: Roo.form,
60918                             listeners : {
60919                                 render : function (_self)
60920                                 {
60921                                     _this.monthField = _self;
60922                                    // _this.monthField.set  today
60923                                 },
60924                                 select : function (combo, date)
60925                                 {
60926                                     _this.grid.ds.load({});
60927                                 }
60928                             },
60929                             value : (function() { return new Date(); })()
60930                         },
60931                         {
60932                             xtype: 'Separator',
60933                             xns: Roo.Toolbar
60934                         },
60935                         {
60936                             xtype: 'TextItem',
60937                             xns: Roo.Toolbar,
60938                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60939                         },
60940                         {
60941                             xtype: 'Fill',
60942                             xns: Roo.Toolbar
60943                         },
60944                         {
60945                             xtype: 'Button',
60946                             xns: Roo.Toolbar,
60947                             listeners : {
60948                                 click : function (_self, e)
60949                                 {
60950                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60951                                     sd.setMonth(sd.getMonth()+1);
60952                                     _this.monthField.setValue(sd.format('Y-m-d'));
60953                                     _this.grid.ds.load({});
60954                                 }
60955                             },
60956                             text : "Next"
60957                         }
60958                     ]
60959                 },
60960                  
60961             }
60962         };
60963         
60964         *//*
60965  * Based on:
60966  * Ext JS Library 1.1.1
60967  * Copyright(c) 2006-2007, Ext JS, LLC.
60968  *
60969  * Originally Released Under LGPL - original licence link has changed is not relivant.
60970  *
60971  * Fork - LGPL
60972  * <script type="text/javascript">
60973  */
60974  
60975 /**
60976  * @class Roo.LoadMask
60977  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60978  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60979  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60980  * element's UpdateManager load indicator and will be destroyed after the initial load.
60981  * @constructor
60982  * Create a new LoadMask
60983  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60984  * @param {Object} config The config object
60985  */
60986 Roo.LoadMask = function(el, config){
60987     this.el = Roo.get(el);
60988     Roo.apply(this, config);
60989     if(this.store){
60990         this.store.on('beforeload', this.onBeforeLoad, this);
60991         this.store.on('load', this.onLoad, this);
60992         this.store.on('loadexception', this.onLoadException, this);
60993         this.removeMask = false;
60994     }else{
60995         var um = this.el.getUpdateManager();
60996         um.showLoadIndicator = false; // disable the default indicator
60997         um.on('beforeupdate', this.onBeforeLoad, this);
60998         um.on('update', this.onLoad, this);
60999         um.on('failure', this.onLoad, this);
61000         this.removeMask = true;
61001     }
61002 };
61003
61004 Roo.LoadMask.prototype = {
61005     /**
61006      * @cfg {Boolean} removeMask
61007      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61008      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61009      */
61010     /**
61011      * @cfg {String} msg
61012      * The text to display in a centered loading message box (defaults to 'Loading...')
61013      */
61014     msg : 'Loading...',
61015     /**
61016      * @cfg {String} msgCls
61017      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61018      */
61019     msgCls : 'x-mask-loading',
61020
61021     /**
61022      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61023      * @type Boolean
61024      */
61025     disabled: false,
61026
61027     /**
61028      * Disables the mask to prevent it from being displayed
61029      */
61030     disable : function(){
61031        this.disabled = true;
61032     },
61033
61034     /**
61035      * Enables the mask so that it can be displayed
61036      */
61037     enable : function(){
61038         this.disabled = false;
61039     },
61040     
61041     onLoadException : function()
61042     {
61043         Roo.log(arguments);
61044         
61045         if (typeof(arguments[3]) != 'undefined') {
61046             Roo.MessageBox.alert("Error loading",arguments[3]);
61047         } 
61048         /*
61049         try {
61050             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61051                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61052             }   
61053         } catch(e) {
61054             
61055         }
61056         */
61057     
61058         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61059     },
61060     // private
61061     onLoad : function()
61062     {
61063         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61064     },
61065
61066     // private
61067     onBeforeLoad : function(){
61068         if(!this.disabled){
61069             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61070         }
61071     },
61072
61073     // private
61074     destroy : function(){
61075         if(this.store){
61076             this.store.un('beforeload', this.onBeforeLoad, this);
61077             this.store.un('load', this.onLoad, this);
61078             this.store.un('loadexception', this.onLoadException, this);
61079         }else{
61080             var um = this.el.getUpdateManager();
61081             um.un('beforeupdate', this.onBeforeLoad, this);
61082             um.un('update', this.onLoad, this);
61083             um.un('failure', this.onLoad, this);
61084         }
61085     }
61086 };/*
61087  * Based on:
61088  * Ext JS Library 1.1.1
61089  * Copyright(c) 2006-2007, Ext JS, LLC.
61090  *
61091  * Originally Released Under LGPL - original licence link has changed is not relivant.
61092  *
61093  * Fork - LGPL
61094  * <script type="text/javascript">
61095  */
61096
61097
61098 /**
61099  * @class Roo.XTemplate
61100  * @extends Roo.Template
61101  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61102 <pre><code>
61103 var t = new Roo.XTemplate(
61104         '&lt;select name="{name}"&gt;',
61105                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61106         '&lt;/select&gt;'
61107 );
61108  
61109 // then append, applying the master template values
61110  </code></pre>
61111  *
61112  * Supported features:
61113  *
61114  *  Tags:
61115
61116 <pre><code>
61117       {a_variable} - output encoded.
61118       {a_variable.format:("Y-m-d")} - call a method on the variable
61119       {a_variable:raw} - unencoded output
61120       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61121       {a_variable:this.method_on_template(...)} - call a method on the template object.
61122  
61123 </code></pre>
61124  *  The tpl tag:
61125 <pre><code>
61126         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61127         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61128         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61129         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61130   
61131         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61132         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61133 </code></pre>
61134  *      
61135  */
61136 Roo.XTemplate = function()
61137 {
61138     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61139     if (this.html) {
61140         this.compile();
61141     }
61142 };
61143
61144
61145 Roo.extend(Roo.XTemplate, Roo.Template, {
61146
61147     /**
61148      * The various sub templates
61149      */
61150     tpls : false,
61151     /**
61152      *
61153      * basic tag replacing syntax
61154      * WORD:WORD()
61155      *
61156      * // you can fake an object call by doing this
61157      *  x.t:(test,tesT) 
61158      * 
61159      */
61160     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61161
61162     /**
61163      * compile the template
61164      *
61165      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61166      *
61167      */
61168     compile: function()
61169     {
61170         var s = this.html;
61171      
61172         s = ['<tpl>', s, '</tpl>'].join('');
61173     
61174         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61175             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61176             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61177             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61178             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61179             m,
61180             id     = 0,
61181             tpls   = [];
61182     
61183         while(true == !!(m = s.match(re))){
61184             var forMatch   = m[0].match(nameRe),
61185                 ifMatch   = m[0].match(ifRe),
61186                 execMatch   = m[0].match(execRe),
61187                 namedMatch   = m[0].match(namedRe),
61188                 
61189                 exp  = null, 
61190                 fn   = null,
61191                 exec = null,
61192                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61193                 
61194             if (ifMatch) {
61195                 // if - puts fn into test..
61196                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61197                 if(exp){
61198                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61199                 }
61200             }
61201             
61202             if (execMatch) {
61203                 // exec - calls a function... returns empty if true is  returned.
61204                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61205                 if(exp){
61206                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61207                 }
61208             }
61209             
61210             
61211             if (name) {
61212                 // for = 
61213                 switch(name){
61214                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61215                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61216                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61217                 }
61218             }
61219             var uid = namedMatch ? namedMatch[1] : id;
61220             
61221             
61222             tpls.push({
61223                 id:     namedMatch ? namedMatch[1] : id,
61224                 target: name,
61225                 exec:   exec,
61226                 test:   fn,
61227                 body:   m[1] || ''
61228             });
61229             if (namedMatch) {
61230                 s = s.replace(m[0], '');
61231             } else { 
61232                 s = s.replace(m[0], '{xtpl'+ id + '}');
61233             }
61234             ++id;
61235         }
61236         this.tpls = [];
61237         for(var i = tpls.length-1; i >= 0; --i){
61238             this.compileTpl(tpls[i]);
61239             this.tpls[tpls[i].id] = tpls[i];
61240         }
61241         this.master = tpls[tpls.length-1];
61242         return this;
61243     },
61244     /**
61245      * same as applyTemplate, except it's done to one of the subTemplates
61246      * when using named templates, you can do:
61247      *
61248      * var str = pl.applySubTemplate('your-name', values);
61249      *
61250      * 
61251      * @param {Number} id of the template
61252      * @param {Object} values to apply to template
61253      * @param {Object} parent (normaly the instance of this object)
61254      */
61255     applySubTemplate : function(id, values, parent)
61256     {
61257         
61258         
61259         var t = this.tpls[id];
61260         
61261         
61262         try { 
61263             if(t.test && !t.test.call(this, values, parent)){
61264                 return '';
61265             }
61266         } catch(e) {
61267             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61268             Roo.log(e.toString());
61269             Roo.log(t.test);
61270             return ''
61271         }
61272         try { 
61273             
61274             if(t.exec && t.exec.call(this, values, parent)){
61275                 return '';
61276             }
61277         } catch(e) {
61278             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61279             Roo.log(e.toString());
61280             Roo.log(t.exec);
61281             return ''
61282         }
61283         try {
61284             var vs = t.target ? t.target.call(this, values, parent) : values;
61285             parent = t.target ? values : parent;
61286             if(t.target && vs instanceof Array){
61287                 var buf = [];
61288                 for(var i = 0, len = vs.length; i < len; i++){
61289                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61290                 }
61291                 return buf.join('');
61292             }
61293             return t.compiled.call(this, vs, parent);
61294         } catch (e) {
61295             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61296             Roo.log(e.toString());
61297             Roo.log(t.compiled);
61298             return '';
61299         }
61300     },
61301
61302     compileTpl : function(tpl)
61303     {
61304         var fm = Roo.util.Format;
61305         var useF = this.disableFormats !== true;
61306         var sep = Roo.isGecko ? "+" : ",";
61307         var undef = function(str) {
61308             Roo.log("Property not found :"  + str);
61309             return '';
61310         };
61311         
61312         var fn = function(m, name, format, args)
61313         {
61314             //Roo.log(arguments);
61315             args = args ? args.replace(/\\'/g,"'") : args;
61316             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61317             if (typeof(format) == 'undefined') {
61318                 format= 'htmlEncode';
61319             }
61320             if (format == 'raw' ) {
61321                 format = false;
61322             }
61323             
61324             if(name.substr(0, 4) == 'xtpl'){
61325                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61326             }
61327             
61328             // build an array of options to determine if value is undefined..
61329             
61330             // basically get 'xxxx.yyyy' then do
61331             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61332             //    (function () { Roo.log("Property not found"); return ''; })() :
61333             //    ......
61334             
61335             var udef_ar = [];
61336             var lookfor = '';
61337             Roo.each(name.split('.'), function(st) {
61338                 lookfor += (lookfor.length ? '.': '') + st;
61339                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
61340             });
61341             
61342             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
61343             
61344             
61345             if(format && useF){
61346                 
61347                 args = args ? ',' + args : "";
61348                  
61349                 if(format.substr(0, 5) != "this."){
61350                     format = "fm." + format + '(';
61351                 }else{
61352                     format = 'this.call("'+ format.substr(5) + '", ';
61353                     args = ", values";
61354                 }
61355                 
61356                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61357             }
61358              
61359             if (args.length) {
61360                 // called with xxyx.yuu:(test,test)
61361                 // change to ()
61362                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61363             }
61364             // raw.. - :raw modifier..
61365             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61366             
61367         };
61368         var body;
61369         // branched to use + in gecko and [].join() in others
61370         if(Roo.isGecko){
61371             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61372                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61373                     "';};};";
61374         }else{
61375             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61376             body.push(tpl.body.replace(/(\r\n|\n)/g,
61377                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61378             body.push("'].join('');};};");
61379             body = body.join('');
61380         }
61381         
61382         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61383        
61384         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61385         eval(body);
61386         
61387         return this;
61388     },
61389
61390     applyTemplate : function(values){
61391         return this.master.compiled.call(this, values, {});
61392         //var s = this.subs;
61393     },
61394
61395     apply : function(){
61396         return this.applyTemplate.apply(this, arguments);
61397     }
61398
61399  });
61400
61401 Roo.XTemplate.from = function(el){
61402     el = Roo.getDom(el);
61403     return new Roo.XTemplate(el.value || el.innerHTML);
61404 };