roojs-debug.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         try {
4671            
4672             if(this.compiled){
4673                 return this.compiled(values);
4674             }
4675             var useF = this.disableFormats !== true;
4676             var fm = Roo.util.Format, tpl = this;
4677             var fn = function(m, name, format, args){
4678                 if(format && useF){
4679                     if(format.substr(0, 5) == "this."){
4680                         return tpl.call(format.substr(5), values[name], values);
4681                     }else{
4682                         if(args){
4683                             // quoted values are required for strings in compiled templates, 
4684                             // but for non compiled we need to strip them
4685                             // quoted reversed for jsmin
4686                             var re = /^\s*['"](.*)["']\s*$/;
4687                             args = args.split(',');
4688                             for(var i = 0, len = args.length; i < len; i++){
4689                                 args[i] = args[i].replace(re, "$1");
4690                             }
4691                             args = [values[name]].concat(args);
4692                         }else{
4693                             args = [values[name]];
4694                         }
4695                         return fm[format].apply(fm, args);
4696                     }
4697                 }else{
4698                     return values[name] !== undefined ? values[name] : "";
4699                 }
4700             };
4701             return this.html.replace(this.re, fn);
4702         } catch (e) {
4703             Roo.log(e);
4704             throw e;
4705         }
4706          
4707     },
4708     
4709     loading : false,
4710       
4711     load : function ()
4712     {
4713          
4714         if (this.loading) {
4715             return;
4716         }
4717         var _t = this;
4718         
4719         this.loading = true;
4720         this.compiled = false;
4721         
4722         var cx = new Roo.data.Connection();
4723         cx.request({
4724             url : this.url,
4725             method : 'GET',
4726             success : function (response) {
4727                 _t.loading = false;
4728                 _t.html = response.responseText;
4729                 _t.url = false;
4730                 _t.compile();
4731              },
4732             failure : function(response) {
4733                 Roo.log("Template failed to load from " + _t.url);
4734                 _t.loading = false;
4735             }
4736         });
4737     },
4738
4739     /**
4740      * Sets the HTML used as the template and optionally compiles it.
4741      * @param {String} html
4742      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4743      * @return {Roo.Template} this
4744      */
4745     set : function(html, compile){
4746         this.html = html;
4747         this.compiled = null;
4748         if(compile){
4749             this.compile();
4750         }
4751         return this;
4752     },
4753     
4754     /**
4755      * True to disable format functions (defaults to false)
4756      * @type Boolean
4757      */
4758     disableFormats : false,
4759     
4760     /**
4761     * The regular expression used to match template variables 
4762     * @type RegExp
4763     * @property 
4764     */
4765     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4766     
4767     /**
4768      * Compiles the template into an internal function, eliminating the RegEx overhead.
4769      * @return {Roo.Template} this
4770      */
4771     compile : function(){
4772         var fm = Roo.util.Format;
4773         var useF = this.disableFormats !== true;
4774         var sep = Roo.isGecko ? "+" : ",";
4775         var fn = function(m, name, format, args){
4776             if(format && useF){
4777                 args = args ? ',' + args : "";
4778                 if(format.substr(0, 5) != "this."){
4779                     format = "fm." + format + '(';
4780                 }else{
4781                     format = 'this.call("'+ format.substr(5) + '", ';
4782                     args = ", values";
4783                 }
4784             }else{
4785                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4786             }
4787             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4788         };
4789         var body;
4790         // branched to use + in gecko and [].join() in others
4791         if(Roo.isGecko){
4792             body = "this.compiled = function(values){ return '" +
4793                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4794                     "';};";
4795         }else{
4796             body = ["this.compiled = function(values){ return ['"];
4797             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4798             body.push("'].join('');};");
4799             body = body.join('');
4800         }
4801         /**
4802          * eval:var:values
4803          * eval:var:fm
4804          */
4805         eval(body);
4806         return this;
4807     },
4808     
4809     // private function used to call members
4810     call : function(fnName, value, allValues){
4811         return this[fnName](value, allValues);
4812     },
4813     
4814     /**
4815      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4816      * @param {String/HTMLElement/Roo.Element} el The context element
4817      * @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'})
4818      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4819      * @return {HTMLElement/Roo.Element} The new node or Element
4820      */
4821     insertFirst: function(el, values, returnElement){
4822         return this.doInsert('afterBegin', el, values, returnElement);
4823     },
4824
4825     /**
4826      * Applies the supplied values to the template and inserts the new node(s) before el.
4827      * @param {String/HTMLElement/Roo.Element} el The context element
4828      * @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'})
4829      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4830      * @return {HTMLElement/Roo.Element} The new node or Element
4831      */
4832     insertBefore: function(el, values, returnElement){
4833         return this.doInsert('beforeBegin', el, values, returnElement);
4834     },
4835
4836     /**
4837      * Applies the supplied values to the template and inserts the new node(s) after el.
4838      * @param {String/HTMLElement/Roo.Element} el The context element
4839      * @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'})
4840      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4841      * @return {HTMLElement/Roo.Element} The new node or Element
4842      */
4843     insertAfter : function(el, values, returnElement){
4844         return this.doInsert('afterEnd', el, values, returnElement);
4845     },
4846     
4847     /**
4848      * Applies the supplied values to the template and appends the new node(s) to el.
4849      * @param {String/HTMLElement/Roo.Element} el The context element
4850      * @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'})
4851      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4852      * @return {HTMLElement/Roo.Element} The new node or Element
4853      */
4854     append : function(el, values, returnElement){
4855         return this.doInsert('beforeEnd', el, values, returnElement);
4856     },
4857
4858     doInsert : function(where, el, values, returnEl){
4859         el = Roo.getDom(el);
4860         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4861         return returnEl ? Roo.get(newNode, true) : newNode;
4862     },
4863
4864     /**
4865      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4866      * @param {String/HTMLElement/Roo.Element} el The context element
4867      * @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'})
4868      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4869      * @return {HTMLElement/Roo.Element} The new node or Element
4870      */
4871     overwrite : function(el, values, returnElement){
4872         el = Roo.getDom(el);
4873         el.innerHTML = this.applyTemplate(values);
4874         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4875     }
4876 };
4877 /**
4878  * Alias for {@link #applyTemplate}
4879  * @method
4880  */
4881 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4882
4883 // backwards compat
4884 Roo.DomHelper.Template = Roo.Template;
4885
4886 /**
4887  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4888  * @param {String/HTMLElement} el A DOM element or its id
4889  * @returns {Roo.Template} The created template
4890  * @static
4891  */
4892 Roo.Template.from = function(el){
4893     el = Roo.getDom(el);
4894     return new Roo.Template(el.value || el.innerHTML);
4895 };/*
4896  * Based on:
4897  * Ext JS Library 1.1.1
4898  * Copyright(c) 2006-2007, Ext JS, LLC.
4899  *
4900  * Originally Released Under LGPL - original licence link has changed is not relivant.
4901  *
4902  * Fork - LGPL
4903  * <script type="text/javascript">
4904  */
4905  
4906
4907 /*
4908  * This is code is also distributed under MIT license for use
4909  * with jQuery and prototype JavaScript libraries.
4910  */
4911 /**
4912  * @class Roo.DomQuery
4913 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).
4914 <p>
4915 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>
4916
4917 <p>
4918 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.
4919 </p>
4920 <h4>Element Selectors:</h4>
4921 <ul class="list">
4922     <li> <b>*</b> any element</li>
4923     <li> <b>E</b> an element with the tag E</li>
4924     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4925     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4926     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4927     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4928 </ul>
4929 <h4>Attribute Selectors:</h4>
4930 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4931 <ul class="list">
4932     <li> <b>E[foo]</b> has an attribute "foo"</li>
4933     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4934     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4935     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4936     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4937     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4938     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4939 </ul>
4940 <h4>Pseudo Classes:</h4>
4941 <ul class="list">
4942     <li> <b>E:first-child</b> E is the first child of its parent</li>
4943     <li> <b>E:last-child</b> E is the last child of its parent</li>
4944     <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>
4945     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4946     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4947     <li> <b>E:only-child</b> E is the only child of its parent</li>
4948     <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>
4949     <li> <b>E:first</b> the first E in the resultset</li>
4950     <li> <b>E:last</b> the last E in the resultset</li>
4951     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4952     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4953     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4954     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4955     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4956     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4957     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4958     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4959     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4960 </ul>
4961 <h4>CSS Value Selectors:</h4>
4962 <ul class="list">
4963     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4964     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4965     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4966     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4967     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4968     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4969 </ul>
4970  * @singleton
4971  */
4972 Roo.DomQuery = function(){
4973     var cache = {}, simpleCache = {}, valueCache = {};
4974     var nonSpace = /\S/;
4975     var trimRe = /^\s+|\s+$/g;
4976     var tplRe = /\{(\d+)\}/g;
4977     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4978     var tagTokenRe = /^(#)?([\w-\*]+)/;
4979     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4980
4981     function child(p, index){
4982         var i = 0;
4983         var n = p.firstChild;
4984         while(n){
4985             if(n.nodeType == 1){
4986                if(++i == index){
4987                    return n;
4988                }
4989             }
4990             n = n.nextSibling;
4991         }
4992         return null;
4993     };
4994
4995     function next(n){
4996         while((n = n.nextSibling) && n.nodeType != 1);
4997         return n;
4998     };
4999
5000     function prev(n){
5001         while((n = n.previousSibling) && n.nodeType != 1);
5002         return n;
5003     };
5004
5005     function children(d){
5006         var n = d.firstChild, ni = -1;
5007             while(n){
5008                 var nx = n.nextSibling;
5009                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5010                     d.removeChild(n);
5011                 }else{
5012                     n.nodeIndex = ++ni;
5013                 }
5014                 n = nx;
5015             }
5016             return this;
5017         };
5018
5019     function byClassName(c, a, v){
5020         if(!v){
5021             return c;
5022         }
5023         var r = [], ri = -1, cn;
5024         for(var i = 0, ci; ci = c[i]; i++){
5025             if((' '+ci.className+' ').indexOf(v) != -1){
5026                 r[++ri] = ci;
5027             }
5028         }
5029         return r;
5030     };
5031
5032     function attrValue(n, attr){
5033         if(!n.tagName && typeof n.length != "undefined"){
5034             n = n[0];
5035         }
5036         if(!n){
5037             return null;
5038         }
5039         if(attr == "for"){
5040             return n.htmlFor;
5041         }
5042         if(attr == "class" || attr == "className"){
5043             return n.className;
5044         }
5045         return n.getAttribute(attr) || n[attr];
5046
5047     };
5048
5049     function getNodes(ns, mode, tagName){
5050         var result = [], ri = -1, cs;
5051         if(!ns){
5052             return result;
5053         }
5054         tagName = tagName || "*";
5055         if(typeof ns.getElementsByTagName != "undefined"){
5056             ns = [ns];
5057         }
5058         if(!mode){
5059             for(var i = 0, ni; ni = ns[i]; i++){
5060                 cs = ni.getElementsByTagName(tagName);
5061                 for(var j = 0, ci; ci = cs[j]; j++){
5062                     result[++ri] = ci;
5063                 }
5064             }
5065         }else if(mode == "/" || mode == ">"){
5066             var utag = tagName.toUpperCase();
5067             for(var i = 0, ni, cn; ni = ns[i]; i++){
5068                 cn = ni.children || ni.childNodes;
5069                 for(var j = 0, cj; cj = cn[j]; j++){
5070                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5071                         result[++ri] = cj;
5072                     }
5073                 }
5074             }
5075         }else if(mode == "+"){
5076             var utag = tagName.toUpperCase();
5077             for(var i = 0, n; n = ns[i]; i++){
5078                 while((n = n.nextSibling) && n.nodeType != 1);
5079                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5080                     result[++ri] = n;
5081                 }
5082             }
5083         }else if(mode == "~"){
5084             for(var i = 0, n; n = ns[i]; i++){
5085                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5086                 if(n){
5087                     result[++ri] = n;
5088                 }
5089             }
5090         }
5091         return result;
5092     };
5093
5094     function concat(a, b){
5095         if(b.slice){
5096             return a.concat(b);
5097         }
5098         for(var i = 0, l = b.length; i < l; i++){
5099             a[a.length] = b[i];
5100         }
5101         return a;
5102     }
5103
5104     function byTag(cs, tagName){
5105         if(cs.tagName || cs == document){
5106             cs = [cs];
5107         }
5108         if(!tagName){
5109             return cs;
5110         }
5111         var r = [], ri = -1;
5112         tagName = tagName.toLowerCase();
5113         for(var i = 0, ci; ci = cs[i]; i++){
5114             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5115                 r[++ri] = ci;
5116             }
5117         }
5118         return r;
5119     };
5120
5121     function byId(cs, attr, id){
5122         if(cs.tagName || cs == document){
5123             cs = [cs];
5124         }
5125         if(!id){
5126             return cs;
5127         }
5128         var r = [], ri = -1;
5129         for(var i = 0,ci; ci = cs[i]; i++){
5130             if(ci && ci.id == id){
5131                 r[++ri] = ci;
5132                 return r;
5133             }
5134         }
5135         return r;
5136     };
5137
5138     function byAttribute(cs, attr, value, op, custom){
5139         var r = [], ri = -1, st = custom=="{";
5140         var f = Roo.DomQuery.operators[op];
5141         for(var i = 0, ci; ci = cs[i]; i++){
5142             var a;
5143             if(st){
5144                 a = Roo.DomQuery.getStyle(ci, attr);
5145             }
5146             else if(attr == "class" || attr == "className"){
5147                 a = ci.className;
5148             }else if(attr == "for"){
5149                 a = ci.htmlFor;
5150             }else if(attr == "href"){
5151                 a = ci.getAttribute("href", 2);
5152             }else{
5153                 a = ci.getAttribute(attr);
5154             }
5155             if((f && f(a, value)) || (!f && a)){
5156                 r[++ri] = ci;
5157             }
5158         }
5159         return r;
5160     };
5161
5162     function byPseudo(cs, name, value){
5163         return Roo.DomQuery.pseudos[name](cs, value);
5164     };
5165
5166     // This is for IE MSXML which does not support expandos.
5167     // IE runs the same speed using setAttribute, however FF slows way down
5168     // and Safari completely fails so they need to continue to use expandos.
5169     var isIE = window.ActiveXObject ? true : false;
5170
5171     // this eval is stop the compressor from
5172     // renaming the variable to something shorter
5173     
5174     /** eval:var:batch */
5175     var batch = 30803; 
5176
5177     var key = 30803;
5178
5179     function nodupIEXml(cs){
5180         var d = ++key;
5181         cs[0].setAttribute("_nodup", d);
5182         var r = [cs[0]];
5183         for(var i = 1, len = cs.length; i < len; i++){
5184             var c = cs[i];
5185             if(!c.getAttribute("_nodup") != d){
5186                 c.setAttribute("_nodup", d);
5187                 r[r.length] = c;
5188             }
5189         }
5190         for(var i = 0, len = cs.length; i < len; i++){
5191             cs[i].removeAttribute("_nodup");
5192         }
5193         return r;
5194     }
5195
5196     function nodup(cs){
5197         if(!cs){
5198             return [];
5199         }
5200         var len = cs.length, c, i, r = cs, cj, ri = -1;
5201         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5202             return cs;
5203         }
5204         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5205             return nodupIEXml(cs);
5206         }
5207         var d = ++key;
5208         cs[0]._nodup = d;
5209         for(i = 1; c = cs[i]; i++){
5210             if(c._nodup != d){
5211                 c._nodup = d;
5212             }else{
5213                 r = [];
5214                 for(var j = 0; j < i; j++){
5215                     r[++ri] = cs[j];
5216                 }
5217                 for(j = i+1; cj = cs[j]; j++){
5218                     if(cj._nodup != d){
5219                         cj._nodup = d;
5220                         r[++ri] = cj;
5221                     }
5222                 }
5223                 return r;
5224             }
5225         }
5226         return r;
5227     }
5228
5229     function quickDiffIEXml(c1, c2){
5230         var d = ++key;
5231         for(var i = 0, len = c1.length; i < len; i++){
5232             c1[i].setAttribute("_qdiff", d);
5233         }
5234         var r = [];
5235         for(var i = 0, len = c2.length; i < len; i++){
5236             if(c2[i].getAttribute("_qdiff") != d){
5237                 r[r.length] = c2[i];
5238             }
5239         }
5240         for(var i = 0, len = c1.length; i < len; i++){
5241            c1[i].removeAttribute("_qdiff");
5242         }
5243         return r;
5244     }
5245
5246     function quickDiff(c1, c2){
5247         var len1 = c1.length;
5248         if(!len1){
5249             return c2;
5250         }
5251         if(isIE && c1[0].selectSingleNode){
5252             return quickDiffIEXml(c1, c2);
5253         }
5254         var d = ++key;
5255         for(var i = 0; i < len1; i++){
5256             c1[i]._qdiff = d;
5257         }
5258         var r = [];
5259         for(var i = 0, len = c2.length; i < len; i++){
5260             if(c2[i]._qdiff != d){
5261                 r[r.length] = c2[i];
5262             }
5263         }
5264         return r;
5265     }
5266
5267     function quickId(ns, mode, root, id){
5268         if(ns == root){
5269            var d = root.ownerDocument || root;
5270            return d.getElementById(id);
5271         }
5272         ns = getNodes(ns, mode, "*");
5273         return byId(ns, null, id);
5274     }
5275
5276     return {
5277         getStyle : function(el, name){
5278             return Roo.fly(el).getStyle(name);
5279         },
5280         /**
5281          * Compiles a selector/xpath query into a reusable function. The returned function
5282          * takes one parameter "root" (optional), which is the context node from where the query should start.
5283          * @param {String} selector The selector/xpath query
5284          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5285          * @return {Function}
5286          */
5287         compile : function(path, type){
5288             type = type || "select";
5289             
5290             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5291             var q = path, mode, lq;
5292             var tk = Roo.DomQuery.matchers;
5293             var tklen = tk.length;
5294             var mm;
5295
5296             // accept leading mode switch
5297             var lmode = q.match(modeRe);
5298             if(lmode && lmode[1]){
5299                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5300                 q = q.replace(lmode[1], "");
5301             }
5302             // strip leading slashes
5303             while(path.substr(0, 1)=="/"){
5304                 path = path.substr(1);
5305             }
5306
5307             while(q && lq != q){
5308                 lq = q;
5309                 var tm = q.match(tagTokenRe);
5310                 if(type == "select"){
5311                     if(tm){
5312                         if(tm[1] == "#"){
5313                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5314                         }else{
5315                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5316                         }
5317                         q = q.replace(tm[0], "");
5318                     }else if(q.substr(0, 1) != '@'){
5319                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5320                     }
5321                 }else{
5322                     if(tm){
5323                         if(tm[1] == "#"){
5324                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5325                         }else{
5326                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5327                         }
5328                         q = q.replace(tm[0], "");
5329                     }
5330                 }
5331                 while(!(mm = q.match(modeRe))){
5332                     var matched = false;
5333                     for(var j = 0; j < tklen; j++){
5334                         var t = tk[j];
5335                         var m = q.match(t.re);
5336                         if(m){
5337                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5338                                                     return m[i];
5339                                                 });
5340                             q = q.replace(m[0], "");
5341                             matched = true;
5342                             break;
5343                         }
5344                     }
5345                     // prevent infinite loop on bad selector
5346                     if(!matched){
5347                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5348                     }
5349                 }
5350                 if(mm[1]){
5351                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5352                     q = q.replace(mm[1], "");
5353                 }
5354             }
5355             fn[fn.length] = "return nodup(n);\n}";
5356             
5357              /** 
5358               * list of variables that need from compression as they are used by eval.
5359              *  eval:var:batch 
5360              *  eval:var:nodup
5361              *  eval:var:byTag
5362              *  eval:var:ById
5363              *  eval:var:getNodes
5364              *  eval:var:quickId
5365              *  eval:var:mode
5366              *  eval:var:root
5367              *  eval:var:n
5368              *  eval:var:byClassName
5369              *  eval:var:byPseudo
5370              *  eval:var:byAttribute
5371              *  eval:var:attrValue
5372              * 
5373              **/ 
5374             eval(fn.join(""));
5375             return f;
5376         },
5377
5378         /**
5379          * Selects a group of elements.
5380          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5381          * @param {Node} root (optional) The start of the query (defaults to document).
5382          * @return {Array}
5383          */
5384         select : function(path, root, type){
5385             if(!root || root == document){
5386                 root = document;
5387             }
5388             if(typeof root == "string"){
5389                 root = document.getElementById(root);
5390             }
5391             var paths = path.split(",");
5392             var results = [];
5393             for(var i = 0, len = paths.length; i < len; i++){
5394                 var p = paths[i].replace(trimRe, "");
5395                 if(!cache[p]){
5396                     cache[p] = Roo.DomQuery.compile(p);
5397                     if(!cache[p]){
5398                         throw p + " is not a valid selector";
5399                     }
5400                 }
5401                 var result = cache[p](root);
5402                 if(result && result != document){
5403                     results = results.concat(result);
5404                 }
5405             }
5406             if(paths.length > 1){
5407                 return nodup(results);
5408             }
5409             return results;
5410         },
5411
5412         /**
5413          * Selects a single element.
5414          * @param {String} selector The selector/xpath query
5415          * @param {Node} root (optional) The start of the query (defaults to document).
5416          * @return {Element}
5417          */
5418         selectNode : function(path, root){
5419             return Roo.DomQuery.select(path, root)[0];
5420         },
5421
5422         /**
5423          * Selects the value of a node, optionally replacing null with the defaultValue.
5424          * @param {String} selector The selector/xpath query
5425          * @param {Node} root (optional) The start of the query (defaults to document).
5426          * @param {String} defaultValue
5427          */
5428         selectValue : function(path, root, defaultValue){
5429             path = path.replace(trimRe, "");
5430             if(!valueCache[path]){
5431                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5432             }
5433             var n = valueCache[path](root);
5434             n = n[0] ? n[0] : n;
5435             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5436             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5437         },
5438
5439         /**
5440          * Selects the value of a node, parsing integers and floats.
5441          * @param {String} selector The selector/xpath query
5442          * @param {Node} root (optional) The start of the query (defaults to document).
5443          * @param {Number} defaultValue
5444          * @return {Number}
5445          */
5446         selectNumber : function(path, root, defaultValue){
5447             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5448             return parseFloat(v);
5449         },
5450
5451         /**
5452          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5453          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5454          * @param {String} selector The simple selector to test
5455          * @return {Boolean}
5456          */
5457         is : function(el, ss){
5458             if(typeof el == "string"){
5459                 el = document.getElementById(el);
5460             }
5461             var isArray = (el instanceof Array);
5462             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5463             return isArray ? (result.length == el.length) : (result.length > 0);
5464         },
5465
5466         /**
5467          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5468          * @param {Array} el An array of elements to filter
5469          * @param {String} selector The simple selector to test
5470          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5471          * the selector instead of the ones that match
5472          * @return {Array}
5473          */
5474         filter : function(els, ss, nonMatches){
5475             ss = ss.replace(trimRe, "");
5476             if(!simpleCache[ss]){
5477                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5478             }
5479             var result = simpleCache[ss](els);
5480             return nonMatches ? quickDiff(result, els) : result;
5481         },
5482
5483         /**
5484          * Collection of matching regular expressions and code snippets.
5485          */
5486         matchers : [{
5487                 re: /^\.([\w-]+)/,
5488                 select: 'n = byClassName(n, null, " {1} ");'
5489             }, {
5490                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5491                 select: 'n = byPseudo(n, "{1}", "{2}");'
5492             },{
5493                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5494                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5495             }, {
5496                 re: /^#([\w-]+)/,
5497                 select: 'n = byId(n, null, "{1}");'
5498             },{
5499                 re: /^@([\w-]+)/,
5500                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5501             }
5502         ],
5503
5504         /**
5505          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5506          * 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;.
5507          */
5508         operators : {
5509             "=" : function(a, v){
5510                 return a == v;
5511             },
5512             "!=" : function(a, v){
5513                 return a != v;
5514             },
5515             "^=" : function(a, v){
5516                 return a && a.substr(0, v.length) == v;
5517             },
5518             "$=" : function(a, v){
5519                 return a && a.substr(a.length-v.length) == v;
5520             },
5521             "*=" : function(a, v){
5522                 return a && a.indexOf(v) !== -1;
5523             },
5524             "%=" : function(a, v){
5525                 return (a % v) == 0;
5526             },
5527             "|=" : function(a, v){
5528                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5529             },
5530             "~=" : function(a, v){
5531                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5532             }
5533         },
5534
5535         /**
5536          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5537          * and the argument (if any) supplied in the selector.
5538          */
5539         pseudos : {
5540             "first-child" : function(c){
5541                 var r = [], ri = -1, n;
5542                 for(var i = 0, ci; ci = n = c[i]; i++){
5543                     while((n = n.previousSibling) && n.nodeType != 1);
5544                     if(!n){
5545                         r[++ri] = ci;
5546                     }
5547                 }
5548                 return r;
5549             },
5550
5551             "last-child" : function(c){
5552                 var r = [], ri = -1, n;
5553                 for(var i = 0, ci; ci = n = c[i]; i++){
5554                     while((n = n.nextSibling) && n.nodeType != 1);
5555                     if(!n){
5556                         r[++ri] = ci;
5557                     }
5558                 }
5559                 return r;
5560             },
5561
5562             "nth-child" : function(c, a) {
5563                 var r = [], ri = -1;
5564                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5565                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5566                 for(var i = 0, n; n = c[i]; i++){
5567                     var pn = n.parentNode;
5568                     if (batch != pn._batch) {
5569                         var j = 0;
5570                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5571                             if(cn.nodeType == 1){
5572                                cn.nodeIndex = ++j;
5573                             }
5574                         }
5575                         pn._batch = batch;
5576                     }
5577                     if (f == 1) {
5578                         if (l == 0 || n.nodeIndex == l){
5579                             r[++ri] = n;
5580                         }
5581                     } else if ((n.nodeIndex + l) % f == 0){
5582                         r[++ri] = n;
5583                     }
5584                 }
5585
5586                 return r;
5587             },
5588
5589             "only-child" : function(c){
5590                 var r = [], ri = -1;;
5591                 for(var i = 0, ci; ci = c[i]; i++){
5592                     if(!prev(ci) && !next(ci)){
5593                         r[++ri] = ci;
5594                     }
5595                 }
5596                 return r;
5597             },
5598
5599             "empty" : function(c){
5600                 var r = [], ri = -1;
5601                 for(var i = 0, ci; ci = c[i]; i++){
5602                     var cns = ci.childNodes, j = 0, cn, empty = true;
5603                     while(cn = cns[j]){
5604                         ++j;
5605                         if(cn.nodeType == 1 || cn.nodeType == 3){
5606                             empty = false;
5607                             break;
5608                         }
5609                     }
5610                     if(empty){
5611                         r[++ri] = ci;
5612                     }
5613                 }
5614                 return r;
5615             },
5616
5617             "contains" : function(c, v){
5618                 var r = [], ri = -1;
5619                 for(var i = 0, ci; ci = c[i]; i++){
5620                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5621                         r[++ri] = ci;
5622                     }
5623                 }
5624                 return r;
5625             },
5626
5627             "nodeValue" : function(c, v){
5628                 var r = [], ri = -1;
5629                 for(var i = 0, ci; ci = c[i]; i++){
5630                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5631                         r[++ri] = ci;
5632                     }
5633                 }
5634                 return r;
5635             },
5636
5637             "checked" : function(c){
5638                 var r = [], ri = -1;
5639                 for(var i = 0, ci; ci = c[i]; i++){
5640                     if(ci.checked == true){
5641                         r[++ri] = ci;
5642                     }
5643                 }
5644                 return r;
5645             },
5646
5647             "not" : function(c, ss){
5648                 return Roo.DomQuery.filter(c, ss, true);
5649             },
5650
5651             "odd" : function(c){
5652                 return this["nth-child"](c, "odd");
5653             },
5654
5655             "even" : function(c){
5656                 return this["nth-child"](c, "even");
5657             },
5658
5659             "nth" : function(c, a){
5660                 return c[a-1] || [];
5661             },
5662
5663             "first" : function(c){
5664                 return c[0] || [];
5665             },
5666
5667             "last" : function(c){
5668                 return c[c.length-1] || [];
5669             },
5670
5671             "has" : function(c, ss){
5672                 var s = Roo.DomQuery.select;
5673                 var r = [], ri = -1;
5674                 for(var i = 0, ci; ci = c[i]; i++){
5675                     if(s(ss, ci).length > 0){
5676                         r[++ri] = ci;
5677                     }
5678                 }
5679                 return r;
5680             },
5681
5682             "next" : function(c, ss){
5683                 var is = Roo.DomQuery.is;
5684                 var r = [], ri = -1;
5685                 for(var i = 0, ci; ci = c[i]; i++){
5686                     var n = next(ci);
5687                     if(n && is(n, ss)){
5688                         r[++ri] = ci;
5689                     }
5690                 }
5691                 return r;
5692             },
5693
5694             "prev" : function(c, ss){
5695                 var is = Roo.DomQuery.is;
5696                 var r = [], ri = -1;
5697                 for(var i = 0, ci; ci = c[i]; i++){
5698                     var n = prev(ci);
5699                     if(n && is(n, ss)){
5700                         r[++ri] = ci;
5701                     }
5702                 }
5703                 return r;
5704             }
5705         }
5706     };
5707 }();
5708
5709 /**
5710  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5711  * @param {String} path The selector/xpath query
5712  * @param {Node} root (optional) The start of the query (defaults to document).
5713  * @return {Array}
5714  * @member Roo
5715  * @method query
5716  */
5717 Roo.query = Roo.DomQuery.select;
5718 /*
5719  * Based on:
5720  * Ext JS Library 1.1.1
5721  * Copyright(c) 2006-2007, Ext JS, LLC.
5722  *
5723  * Originally Released Under LGPL - original licence link has changed is not relivant.
5724  *
5725  * Fork - LGPL
5726  * <script type="text/javascript">
5727  */
5728
5729 /**
5730  * @class Roo.util.Observable
5731  * Base class that provides a common interface for publishing events. Subclasses are expected to
5732  * to have a property "events" with all the events defined.<br>
5733  * For example:
5734  * <pre><code>
5735  Employee = function(name){
5736     this.name = name;
5737     this.addEvents({
5738         "fired" : true,
5739         "quit" : true
5740     });
5741  }
5742  Roo.extend(Employee, Roo.util.Observable);
5743 </code></pre>
5744  * @param {Object} config properties to use (incuding events / listeners)
5745  */
5746
5747 Roo.util.Observable = function(cfg){
5748     
5749     cfg = cfg|| {};
5750     this.addEvents(cfg.events || {});
5751     if (cfg.events) {
5752         delete cfg.events; // make sure
5753     }
5754      
5755     Roo.apply(this, cfg);
5756     
5757     if(this.listeners){
5758         this.on(this.listeners);
5759         delete this.listeners;
5760     }
5761 };
5762 Roo.util.Observable.prototype = {
5763     /** 
5764  * @cfg {Object} listeners  list of events and functions to call for this object, 
5765  * For example :
5766  * <pre><code>
5767     listeners :  { 
5768        'click' : function(e) {
5769            ..... 
5770         } ,
5771         .... 
5772     } 
5773   </code></pre>
5774  */
5775     
5776     
5777     /**
5778      * Fires the specified event with the passed parameters (minus the event name).
5779      * @param {String} eventName
5780      * @param {Object...} args Variable number of parameters are passed to handlers
5781      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5782      */
5783     fireEvent : function(){
5784         var ce = this.events[arguments[0].toLowerCase()];
5785         if(typeof ce == "object"){
5786             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5787         }else{
5788             return true;
5789         }
5790     },
5791
5792     // private
5793     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5794
5795     /**
5796      * Appends an event handler to this component
5797      * @param {String}   eventName The type of event to listen for
5798      * @param {Function} handler The method the event invokes
5799      * @param {Object}   scope (optional) The scope in which to execute the handler
5800      * function. The handler function's "this" context.
5801      * @param {Object}   options (optional) An object containing handler configuration
5802      * properties. This may contain any of the following properties:<ul>
5803      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5804      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5805      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5806      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5807      * by the specified number of milliseconds. If the event fires again within that time, the original
5808      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5809      * </ul><br>
5810      * <p>
5811      * <b>Combining Options</b><br>
5812      * Using the options argument, it is possible to combine different types of listeners:<br>
5813      * <br>
5814      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5815                 <pre><code>
5816                 el.on('click', this.onClick, this, {
5817                         single: true,
5818                 delay: 100,
5819                 forumId: 4
5820                 });
5821                 </code></pre>
5822      * <p>
5823      * <b>Attaching multiple handlers in 1 call</b><br>
5824      * The method also allows for a single argument to be passed which is a config object containing properties
5825      * which specify multiple handlers.
5826      * <pre><code>
5827                 el.on({
5828                         'click': {
5829                         fn: this.onClick,
5830                         scope: this,
5831                         delay: 100
5832                 }, 
5833                 'mouseover': {
5834                         fn: this.onMouseOver,
5835                         scope: this
5836                 },
5837                 'mouseout': {
5838                         fn: this.onMouseOut,
5839                         scope: this
5840                 }
5841                 });
5842                 </code></pre>
5843      * <p>
5844      * Or a shorthand syntax which passes the same scope object to all handlers:
5845         <pre><code>
5846                 el.on({
5847                         'click': this.onClick,
5848                 'mouseover': this.onMouseOver,
5849                 'mouseout': this.onMouseOut,
5850                 scope: this
5851                 });
5852                 </code></pre>
5853      */
5854     addListener : function(eventName, fn, scope, o){
5855         if(typeof eventName == "object"){
5856             o = eventName;
5857             for(var e in o){
5858                 if(this.filterOptRe.test(e)){
5859                     continue;
5860                 }
5861                 if(typeof o[e] == "function"){
5862                     // shared options
5863                     this.addListener(e, o[e], o.scope,  o);
5864                 }else{
5865                     // individual options
5866                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5867                 }
5868             }
5869             return;
5870         }
5871         o = (!o || typeof o == "boolean") ? {} : o;
5872         eventName = eventName.toLowerCase();
5873         var ce = this.events[eventName] || true;
5874         if(typeof ce == "boolean"){
5875             ce = new Roo.util.Event(this, eventName);
5876             this.events[eventName] = ce;
5877         }
5878         ce.addListener(fn, scope, o);
5879     },
5880
5881     /**
5882      * Removes a listener
5883      * @param {String}   eventName     The type of event to listen for
5884      * @param {Function} handler        The handler to remove
5885      * @param {Object}   scope  (optional) The scope (this object) for the handler
5886      */
5887     removeListener : function(eventName, fn, scope){
5888         var ce = this.events[eventName.toLowerCase()];
5889         if(typeof ce == "object"){
5890             ce.removeListener(fn, scope);
5891         }
5892     },
5893
5894     /**
5895      * Removes all listeners for this object
5896      */
5897     purgeListeners : function(){
5898         for(var evt in this.events){
5899             if(typeof this.events[evt] == "object"){
5900                  this.events[evt].clearListeners();
5901             }
5902         }
5903     },
5904
5905     relayEvents : function(o, events){
5906         var createHandler = function(ename){
5907             return function(){
5908                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5909             };
5910         };
5911         for(var i = 0, len = events.length; i < len; i++){
5912             var ename = events[i];
5913             if(!this.events[ename]){ this.events[ename] = true; };
5914             o.on(ename, createHandler(ename), this);
5915         }
5916     },
5917
5918     /**
5919      * Used to define events on this Observable
5920      * @param {Object} object The object with the events defined
5921      */
5922     addEvents : function(o){
5923         if(!this.events){
5924             this.events = {};
5925         }
5926         Roo.applyIf(this.events, o);
5927     },
5928
5929     /**
5930      * Checks to see if this object has any listeners for a specified event
5931      * @param {String} eventName The name of the event to check for
5932      * @return {Boolean} True if the event is being listened for, else false
5933      */
5934     hasListener : function(eventName){
5935         var e = this.events[eventName];
5936         return typeof e == "object" && e.listeners.length > 0;
5937     }
5938 };
5939 /**
5940  * Appends an event handler to this element (shorthand for addListener)
5941  * @param {String}   eventName     The type of event to listen for
5942  * @param {Function} handler        The method the event invokes
5943  * @param {Object}   scope (optional) The scope in which to execute the handler
5944  * function. The handler function's "this" context.
5945  * @param {Object}   options  (optional)
5946  * @method
5947  */
5948 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5949 /**
5950  * Removes a listener (shorthand for removeListener)
5951  * @param {String}   eventName     The type of event to listen for
5952  * @param {Function} handler        The handler to remove
5953  * @param {Object}   scope  (optional) The scope (this object) for the handler
5954  * @method
5955  */
5956 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5957
5958 /**
5959  * Starts capture on the specified Observable. All events will be passed
5960  * to the supplied function with the event name + standard signature of the event
5961  * <b>before</b> the event is fired. If the supplied function returns false,
5962  * the event will not fire.
5963  * @param {Observable} o The Observable to capture
5964  * @param {Function} fn The function to call
5965  * @param {Object} scope (optional) The scope (this object) for the fn
5966  * @static
5967  */
5968 Roo.util.Observable.capture = function(o, fn, scope){
5969     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5970 };
5971
5972 /**
5973  * Removes <b>all</b> added captures from the Observable.
5974  * @param {Observable} o The Observable to release
5975  * @static
5976  */
5977 Roo.util.Observable.releaseCapture = function(o){
5978     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5979 };
5980
5981 (function(){
5982
5983     var createBuffered = function(h, o, scope){
5984         var task = new Roo.util.DelayedTask();
5985         return function(){
5986             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5987         };
5988     };
5989
5990     var createSingle = function(h, e, fn, scope){
5991         return function(){
5992             e.removeListener(fn, scope);
5993             return h.apply(scope, arguments);
5994         };
5995     };
5996
5997     var createDelayed = function(h, o, scope){
5998         return function(){
5999             var args = Array.prototype.slice.call(arguments, 0);
6000             setTimeout(function(){
6001                 h.apply(scope, args);
6002             }, o.delay || 10);
6003         };
6004     };
6005
6006     Roo.util.Event = function(obj, name){
6007         this.name = name;
6008         this.obj = obj;
6009         this.listeners = [];
6010     };
6011
6012     Roo.util.Event.prototype = {
6013         addListener : function(fn, scope, options){
6014             var o = options || {};
6015             scope = scope || this.obj;
6016             if(!this.isListening(fn, scope)){
6017                 var l = {fn: fn, scope: scope, options: o};
6018                 var h = fn;
6019                 if(o.delay){
6020                     h = createDelayed(h, o, scope);
6021                 }
6022                 if(o.single){
6023                     h = createSingle(h, this, fn, scope);
6024                 }
6025                 if(o.buffer){
6026                     h = createBuffered(h, o, scope);
6027                 }
6028                 l.fireFn = h;
6029                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6030                     this.listeners.push(l);
6031                 }else{
6032                     this.listeners = this.listeners.slice(0);
6033                     this.listeners.push(l);
6034                 }
6035             }
6036         },
6037
6038         findListener : function(fn, scope){
6039             scope = scope || this.obj;
6040             var ls = this.listeners;
6041             for(var i = 0, len = ls.length; i < len; i++){
6042                 var l = ls[i];
6043                 if(l.fn == fn && l.scope == scope){
6044                     return i;
6045                 }
6046             }
6047             return -1;
6048         },
6049
6050         isListening : function(fn, scope){
6051             return this.findListener(fn, scope) != -1;
6052         },
6053
6054         removeListener : function(fn, scope){
6055             var index;
6056             if((index = this.findListener(fn, scope)) != -1){
6057                 if(!this.firing){
6058                     this.listeners.splice(index, 1);
6059                 }else{
6060                     this.listeners = this.listeners.slice(0);
6061                     this.listeners.splice(index, 1);
6062                 }
6063                 return true;
6064             }
6065             return false;
6066         },
6067
6068         clearListeners : function(){
6069             this.listeners = [];
6070         },
6071
6072         fire : function(){
6073             var ls = this.listeners, scope, len = ls.length;
6074             if(len > 0){
6075                 this.firing = true;
6076                 var args = Array.prototype.slice.call(arguments, 0);
6077                 for(var i = 0; i < len; i++){
6078                     var l = ls[i];
6079                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6080                         this.firing = false;
6081                         return false;
6082                     }
6083                 }
6084                 this.firing = false;
6085             }
6086             return true;
6087         }
6088     };
6089 })();/*
6090  * RooJS Library 
6091  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6092  *
6093  * Licence LGPL 
6094  *
6095  */
6096  
6097 /**
6098  * @class Roo.Document
6099  * @extends Roo.util.Observable
6100  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6101  * 
6102  * @param {Object} config the methods and properties of the 'base' class for the application.
6103  * 
6104  *  Generic Page handler - implement this to start your app..
6105  * 
6106  * eg.
6107  *  MyProject = new Roo.Document({
6108         events : {
6109             'load' : true // your events..
6110         },
6111         listeners : {
6112             'ready' : function() {
6113                 // fired on Roo.onReady()
6114             }
6115         }
6116  * 
6117  */
6118 Roo.Document = function(cfg) {
6119      
6120     this.addEvents({ 
6121         'ready' : true
6122     });
6123     Roo.util.Observable.call(this,cfg);
6124     
6125     var _this = this;
6126     
6127     Roo.onReady(function() {
6128         _this.fireEvent('ready');
6129     },null,false);
6130     
6131     
6132 }
6133
6134 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6135  * Based on:
6136  * Ext JS Library 1.1.1
6137  * Copyright(c) 2006-2007, Ext JS, LLC.
6138  *
6139  * Originally Released Under LGPL - original licence link has changed is not relivant.
6140  *
6141  * Fork - LGPL
6142  * <script type="text/javascript">
6143  */
6144
6145 /**
6146  * @class Roo.EventManager
6147  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6148  * several useful events directly.
6149  * See {@link Roo.EventObject} for more details on normalized event objects.
6150  * @singleton
6151  */
6152 Roo.EventManager = function(){
6153     var docReadyEvent, docReadyProcId, docReadyState = false;
6154     var resizeEvent, resizeTask, textEvent, textSize;
6155     var E = Roo.lib.Event;
6156     var D = Roo.lib.Dom;
6157
6158     
6159     
6160
6161     var fireDocReady = function(){
6162         if(!docReadyState){
6163             docReadyState = true;
6164             Roo.isReady = true;
6165             if(docReadyProcId){
6166                 clearInterval(docReadyProcId);
6167             }
6168             if(Roo.isGecko || Roo.isOpera) {
6169                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6170             }
6171             if(Roo.isIE){
6172                 var defer = document.getElementById("ie-deferred-loader");
6173                 if(defer){
6174                     defer.onreadystatechange = null;
6175                     defer.parentNode.removeChild(defer);
6176                 }
6177             }
6178             if(docReadyEvent){
6179                 docReadyEvent.fire();
6180                 docReadyEvent.clearListeners();
6181             }
6182         }
6183     };
6184     
6185     var initDocReady = function(){
6186         docReadyEvent = new Roo.util.Event();
6187         if(Roo.isGecko || Roo.isOpera) {
6188             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6189         }else if(Roo.isIE){
6190             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6191             var defer = document.getElementById("ie-deferred-loader");
6192             defer.onreadystatechange = function(){
6193                 if(this.readyState == "complete"){
6194                     fireDocReady();
6195                 }
6196             };
6197         }else if(Roo.isSafari){ 
6198             docReadyProcId = setInterval(function(){
6199                 var rs = document.readyState;
6200                 if(rs == "complete") {
6201                     fireDocReady();     
6202                  }
6203             }, 10);
6204         }
6205         // no matter what, make sure it fires on load
6206         E.on(window, "load", fireDocReady);
6207     };
6208
6209     var createBuffered = function(h, o){
6210         var task = new Roo.util.DelayedTask(h);
6211         return function(e){
6212             // create new event object impl so new events don't wipe out properties
6213             e = new Roo.EventObjectImpl(e);
6214             task.delay(o.buffer, h, null, [e]);
6215         };
6216     };
6217
6218     var createSingle = function(h, el, ename, fn){
6219         return function(e){
6220             Roo.EventManager.removeListener(el, ename, fn);
6221             h(e);
6222         };
6223     };
6224
6225     var createDelayed = function(h, o){
6226         return function(e){
6227             // create new event object impl so new events don't wipe out properties
6228             e = new Roo.EventObjectImpl(e);
6229             setTimeout(function(){
6230                 h(e);
6231             }, o.delay || 10);
6232         };
6233     };
6234     var transitionEndVal = false;
6235     
6236     var transitionEnd = function()
6237     {
6238         if (transitionEndVal) {
6239             return transitionEndVal;
6240         }
6241         var el = document.createElement('div');
6242
6243         var transEndEventNames = {
6244             WebkitTransition : 'webkitTransitionEnd',
6245             MozTransition    : 'transitionend',
6246             OTransition      : 'oTransitionEnd otransitionend',
6247             transition       : 'transitionend'
6248         };
6249     
6250         for (var name in transEndEventNames) {
6251             if (el.style[name] !== undefined) {
6252                 transitionEndVal = transEndEventNames[name];
6253                 return  transitionEndVal ;
6254             }
6255         }
6256     }
6257     
6258
6259     var listen = function(element, ename, opt, fn, scope){
6260         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6261         fn = fn || o.fn; scope = scope || o.scope;
6262         var el = Roo.getDom(element);
6263         
6264         
6265         if(!el){
6266             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6267         }
6268         
6269         if (ename == 'transitionend') {
6270             ename = transitionEnd();
6271         }
6272         var h = function(e){
6273             e = Roo.EventObject.setEvent(e);
6274             var t;
6275             if(o.delegate){
6276                 t = e.getTarget(o.delegate, el);
6277                 if(!t){
6278                     return;
6279                 }
6280             }else{
6281                 t = e.target;
6282             }
6283             if(o.stopEvent === true){
6284                 e.stopEvent();
6285             }
6286             if(o.preventDefault === true){
6287                e.preventDefault();
6288             }
6289             if(o.stopPropagation === true){
6290                 e.stopPropagation();
6291             }
6292
6293             if(o.normalized === false){
6294                 e = e.browserEvent;
6295             }
6296
6297             fn.call(scope || el, e, t, o);
6298         };
6299         if(o.delay){
6300             h = createDelayed(h, o);
6301         }
6302         if(o.single){
6303             h = createSingle(h, el, ename, fn);
6304         }
6305         if(o.buffer){
6306             h = createBuffered(h, o);
6307         }
6308         
6309         fn._handlers = fn._handlers || [];
6310         
6311         
6312         fn._handlers.push([Roo.id(el), ename, h]);
6313         
6314         
6315          
6316         E.on(el, ename, h);
6317         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6318             el.addEventListener("DOMMouseScroll", h, false);
6319             E.on(window, 'unload', function(){
6320                 el.removeEventListener("DOMMouseScroll", h, false);
6321             });
6322         }
6323         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6324             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6325         }
6326         return h;
6327     };
6328
6329     var stopListening = function(el, ename, fn){
6330         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6331         if(hds){
6332             for(var i = 0, len = hds.length; i < len; i++){
6333                 var h = hds[i];
6334                 if(h[0] == id && h[1] == ename){
6335                     hd = h[2];
6336                     hds.splice(i, 1);
6337                     break;
6338                 }
6339             }
6340         }
6341         E.un(el, ename, hd);
6342         el = Roo.getDom(el);
6343         if(ename == "mousewheel" && el.addEventListener){
6344             el.removeEventListener("DOMMouseScroll", hd, false);
6345         }
6346         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6347             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6348         }
6349     };
6350
6351     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6352     
6353     var pub = {
6354         
6355         
6356         /** 
6357          * Fix for doc tools
6358          * @scope Roo.EventManager
6359          */
6360         
6361         
6362         /** 
6363          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6364          * object with a Roo.EventObject
6365          * @param {Function} fn        The method the event invokes
6366          * @param {Object}   scope    An object that becomes the scope of the handler
6367          * @param {boolean}  override If true, the obj passed in becomes
6368          *                             the execution scope of the listener
6369          * @return {Function} The wrapped function
6370          * @deprecated
6371          */
6372         wrap : function(fn, scope, override){
6373             return function(e){
6374                 Roo.EventObject.setEvent(e);
6375                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6376             };
6377         },
6378         
6379         /**
6380      * Appends an event handler to an element (shorthand for addListener)
6381      * @param {String/HTMLElement}   element        The html element or id to assign the
6382      * @param {String}   eventName The type of event to listen for
6383      * @param {Function} handler The method the event invokes
6384      * @param {Object}   scope (optional) The scope in which to execute the handler
6385      * function. The handler function's "this" context.
6386      * @param {Object}   options (optional) An object containing handler configuration
6387      * properties. This may contain any of the following properties:<ul>
6388      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6389      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6390      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6391      * <li>preventDefault {Boolean} True to prevent the default action</li>
6392      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6393      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6394      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6395      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6396      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6397      * by the specified number of milliseconds. If the event fires again within that time, the original
6398      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6399      * </ul><br>
6400      * <p>
6401      * <b>Combining Options</b><br>
6402      * Using the options argument, it is possible to combine different types of listeners:<br>
6403      * <br>
6404      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6405      * Code:<pre><code>
6406 el.on('click', this.onClick, this, {
6407     single: true,
6408     delay: 100,
6409     stopEvent : true,
6410     forumId: 4
6411 });</code></pre>
6412      * <p>
6413      * <b>Attaching multiple handlers in 1 call</b><br>
6414       * The method also allows for a single argument to be passed which is a config object containing properties
6415      * which specify multiple handlers.
6416      * <p>
6417      * Code:<pre><code>
6418 el.on({
6419     'click' : {
6420         fn: this.onClick
6421         scope: this,
6422         delay: 100
6423     },
6424     'mouseover' : {
6425         fn: this.onMouseOver
6426         scope: this
6427     },
6428     'mouseout' : {
6429         fn: this.onMouseOut
6430         scope: this
6431     }
6432 });</code></pre>
6433      * <p>
6434      * Or a shorthand syntax:<br>
6435      * Code:<pre><code>
6436 el.on({
6437     'click' : this.onClick,
6438     'mouseover' : this.onMouseOver,
6439     'mouseout' : this.onMouseOut
6440     scope: this
6441 });</code></pre>
6442      */
6443         addListener : function(element, eventName, fn, scope, options){
6444             if(typeof eventName == "object"){
6445                 var o = eventName;
6446                 for(var e in o){
6447                     if(propRe.test(e)){
6448                         continue;
6449                     }
6450                     if(typeof o[e] == "function"){
6451                         // shared options
6452                         listen(element, e, o, o[e], o.scope);
6453                     }else{
6454                         // individual options
6455                         listen(element, e, o[e]);
6456                     }
6457                 }
6458                 return;
6459             }
6460             return listen(element, eventName, options, fn, scope);
6461         },
6462         
6463         /**
6464          * Removes an event handler
6465          *
6466          * @param {String/HTMLElement}   element        The id or html element to remove the 
6467          *                             event from
6468          * @param {String}   eventName     The type of event
6469          * @param {Function} fn
6470          * @return {Boolean} True if a listener was actually removed
6471          */
6472         removeListener : function(element, eventName, fn){
6473             return stopListening(element, eventName, fn);
6474         },
6475         
6476         /**
6477          * Fires when the document is ready (before onload and before images are loaded). Can be 
6478          * accessed shorthanded Roo.onReady().
6479          * @param {Function} fn        The method the event invokes
6480          * @param {Object}   scope    An  object that becomes the scope of the handler
6481          * @param {boolean}  options
6482          */
6483         onDocumentReady : function(fn, scope, options){
6484             if(docReadyState){ // if it already fired
6485                 docReadyEvent.addListener(fn, scope, options);
6486                 docReadyEvent.fire();
6487                 docReadyEvent.clearListeners();
6488                 return;
6489             }
6490             if(!docReadyEvent){
6491                 initDocReady();
6492             }
6493             docReadyEvent.addListener(fn, scope, options);
6494         },
6495         
6496         /**
6497          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6498          * @param {Function} fn        The method the event invokes
6499          * @param {Object}   scope    An object that becomes the scope of the handler
6500          * @param {boolean}  options
6501          */
6502         onWindowResize : function(fn, scope, options){
6503             if(!resizeEvent){
6504                 resizeEvent = new Roo.util.Event();
6505                 resizeTask = new Roo.util.DelayedTask(function(){
6506                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6507                 });
6508                 E.on(window, "resize", function(){
6509                     if(Roo.isIE){
6510                         resizeTask.delay(50);
6511                     }else{
6512                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6513                     }
6514                 });
6515             }
6516             resizeEvent.addListener(fn, scope, options);
6517         },
6518
6519         /**
6520          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6521          * @param {Function} fn        The method the event invokes
6522          * @param {Object}   scope    An object that becomes the scope of the handler
6523          * @param {boolean}  options
6524          */
6525         onTextResize : function(fn, scope, options){
6526             if(!textEvent){
6527                 textEvent = new Roo.util.Event();
6528                 var textEl = new Roo.Element(document.createElement('div'));
6529                 textEl.dom.className = 'x-text-resize';
6530                 textEl.dom.innerHTML = 'X';
6531                 textEl.appendTo(document.body);
6532                 textSize = textEl.dom.offsetHeight;
6533                 setInterval(function(){
6534                     if(textEl.dom.offsetHeight != textSize){
6535                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6536                     }
6537                 }, this.textResizeInterval);
6538             }
6539             textEvent.addListener(fn, scope, options);
6540         },
6541
6542         /**
6543          * Removes the passed window resize listener.
6544          * @param {Function} fn        The method the event invokes
6545          * @param {Object}   scope    The scope of handler
6546          */
6547         removeResizeListener : function(fn, scope){
6548             if(resizeEvent){
6549                 resizeEvent.removeListener(fn, scope);
6550             }
6551         },
6552
6553         // private
6554         fireResize : function(){
6555             if(resizeEvent){
6556                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6557             }   
6558         },
6559         /**
6560          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6561          */
6562         ieDeferSrc : false,
6563         /**
6564          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6565          */
6566         textResizeInterval : 50
6567     };
6568     
6569     /**
6570      * Fix for doc tools
6571      * @scopeAlias pub=Roo.EventManager
6572      */
6573     
6574      /**
6575      * Appends an event handler to an element (shorthand for addListener)
6576      * @param {String/HTMLElement}   element        The html element or id to assign the
6577      * @param {String}   eventName The type of event to listen for
6578      * @param {Function} handler The method the event invokes
6579      * @param {Object}   scope (optional) The scope in which to execute the handler
6580      * function. The handler function's "this" context.
6581      * @param {Object}   options (optional) An object containing handler configuration
6582      * properties. This may contain any of the following properties:<ul>
6583      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6584      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6585      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6586      * <li>preventDefault {Boolean} True to prevent the default action</li>
6587      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6588      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6589      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6590      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6591      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6592      * by the specified number of milliseconds. If the event fires again within that time, the original
6593      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6594      * </ul><br>
6595      * <p>
6596      * <b>Combining Options</b><br>
6597      * Using the options argument, it is possible to combine different types of listeners:<br>
6598      * <br>
6599      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6600      * Code:<pre><code>
6601 el.on('click', this.onClick, this, {
6602     single: true,
6603     delay: 100,
6604     stopEvent : true,
6605     forumId: 4
6606 });</code></pre>
6607      * <p>
6608      * <b>Attaching multiple handlers in 1 call</b><br>
6609       * The method also allows for a single argument to be passed which is a config object containing properties
6610      * which specify multiple handlers.
6611      * <p>
6612      * Code:<pre><code>
6613 el.on({
6614     'click' : {
6615         fn: this.onClick
6616         scope: this,
6617         delay: 100
6618     },
6619     'mouseover' : {
6620         fn: this.onMouseOver
6621         scope: this
6622     },
6623     'mouseout' : {
6624         fn: this.onMouseOut
6625         scope: this
6626     }
6627 });</code></pre>
6628      * <p>
6629      * Or a shorthand syntax:<br>
6630      * Code:<pre><code>
6631 el.on({
6632     'click' : this.onClick,
6633     'mouseover' : this.onMouseOver,
6634     'mouseout' : this.onMouseOut
6635     scope: this
6636 });</code></pre>
6637      */
6638     pub.on = pub.addListener;
6639     pub.un = pub.removeListener;
6640
6641     pub.stoppedMouseDownEvent = new Roo.util.Event();
6642     return pub;
6643 }();
6644 /**
6645   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6646   * @param {Function} fn        The method the event invokes
6647   * @param {Object}   scope    An  object that becomes the scope of the handler
6648   * @param {boolean}  override If true, the obj passed in becomes
6649   *                             the execution scope of the listener
6650   * @member Roo
6651   * @method onReady
6652  */
6653 Roo.onReady = Roo.EventManager.onDocumentReady;
6654
6655 Roo.onReady(function(){
6656     var bd = Roo.get(document.body);
6657     if(!bd){ return; }
6658
6659     var cls = [
6660             Roo.isIE ? "roo-ie"
6661             : Roo.isIE11 ? "roo-ie11"
6662             : Roo.isEdge ? "roo-edge"
6663             : Roo.isGecko ? "roo-gecko"
6664             : Roo.isOpera ? "roo-opera"
6665             : Roo.isSafari ? "roo-safari" : ""];
6666
6667     if(Roo.isMac){
6668         cls.push("roo-mac");
6669     }
6670     if(Roo.isLinux){
6671         cls.push("roo-linux");
6672     }
6673     if(Roo.isIOS){
6674         cls.push("roo-ios");
6675     }
6676     if(Roo.isTouch){
6677         cls.push("roo-touch");
6678     }
6679     if(Roo.isBorderBox){
6680         cls.push('roo-border-box');
6681     }
6682     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6683         var p = bd.dom.parentNode;
6684         if(p){
6685             p.className += ' roo-strict';
6686         }
6687     }
6688     bd.addClass(cls.join(' '));
6689 });
6690
6691 /**
6692  * @class Roo.EventObject
6693  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6694  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6695  * Example:
6696  * <pre><code>
6697  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6698     e.preventDefault();
6699     var target = e.getTarget();
6700     ...
6701  }
6702  var myDiv = Roo.get("myDiv");
6703  myDiv.on("click", handleClick);
6704  //or
6705  Roo.EventManager.on("myDiv", 'click', handleClick);
6706  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6707  </code></pre>
6708  * @singleton
6709  */
6710 Roo.EventObject = function(){
6711     
6712     var E = Roo.lib.Event;
6713     
6714     // safari keypress events for special keys return bad keycodes
6715     var safariKeys = {
6716         63234 : 37, // left
6717         63235 : 39, // right
6718         63232 : 38, // up
6719         63233 : 40, // down
6720         63276 : 33, // page up
6721         63277 : 34, // page down
6722         63272 : 46, // delete
6723         63273 : 36, // home
6724         63275 : 35  // end
6725     };
6726
6727     // normalize button clicks
6728     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6729                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6730
6731     Roo.EventObjectImpl = function(e){
6732         if(e){
6733             this.setEvent(e.browserEvent || e);
6734         }
6735     };
6736     Roo.EventObjectImpl.prototype = {
6737         /**
6738          * Used to fix doc tools.
6739          * @scope Roo.EventObject.prototype
6740          */
6741             
6742
6743         
6744         
6745         /** The normal browser event */
6746         browserEvent : null,
6747         /** The button pressed in a mouse event */
6748         button : -1,
6749         /** True if the shift key was down during the event */
6750         shiftKey : false,
6751         /** True if the control key was down during the event */
6752         ctrlKey : false,
6753         /** True if the alt key was down during the event */
6754         altKey : false,
6755
6756         /** Key constant 
6757         * @type Number */
6758         BACKSPACE : 8,
6759         /** Key constant 
6760         * @type Number */
6761         TAB : 9,
6762         /** Key constant 
6763         * @type Number */
6764         RETURN : 13,
6765         /** Key constant 
6766         * @type Number */
6767         ENTER : 13,
6768         /** Key constant 
6769         * @type Number */
6770         SHIFT : 16,
6771         /** Key constant 
6772         * @type Number */
6773         CONTROL : 17,
6774         /** Key constant 
6775         * @type Number */
6776         ESC : 27,
6777         /** Key constant 
6778         * @type Number */
6779         SPACE : 32,
6780         /** Key constant 
6781         * @type Number */
6782         PAGEUP : 33,
6783         /** Key constant 
6784         * @type Number */
6785         PAGEDOWN : 34,
6786         /** Key constant 
6787         * @type Number */
6788         END : 35,
6789         /** Key constant 
6790         * @type Number */
6791         HOME : 36,
6792         /** Key constant 
6793         * @type Number */
6794         LEFT : 37,
6795         /** Key constant 
6796         * @type Number */
6797         UP : 38,
6798         /** Key constant 
6799         * @type Number */
6800         RIGHT : 39,
6801         /** Key constant 
6802         * @type Number */
6803         DOWN : 40,
6804         /** Key constant 
6805         * @type Number */
6806         DELETE : 46,
6807         /** Key constant 
6808         * @type Number */
6809         F5 : 116,
6810
6811            /** @private */
6812         setEvent : function(e){
6813             if(e == this || (e && e.browserEvent)){ // already wrapped
6814                 return e;
6815             }
6816             this.browserEvent = e;
6817             if(e){
6818                 // normalize buttons
6819                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6820                 if(e.type == 'click' && this.button == -1){
6821                     this.button = 0;
6822                 }
6823                 this.type = e.type;
6824                 this.shiftKey = e.shiftKey;
6825                 // mac metaKey behaves like ctrlKey
6826                 this.ctrlKey = e.ctrlKey || e.metaKey;
6827                 this.altKey = e.altKey;
6828                 // in getKey these will be normalized for the mac
6829                 this.keyCode = e.keyCode;
6830                 // keyup warnings on firefox.
6831                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6832                 // cache the target for the delayed and or buffered events
6833                 this.target = E.getTarget(e);
6834                 // same for XY
6835                 this.xy = E.getXY(e);
6836             }else{
6837                 this.button = -1;
6838                 this.shiftKey = false;
6839                 this.ctrlKey = false;
6840                 this.altKey = false;
6841                 this.keyCode = 0;
6842                 this.charCode =0;
6843                 this.target = null;
6844                 this.xy = [0, 0];
6845             }
6846             return this;
6847         },
6848
6849         /**
6850          * Stop the event (preventDefault and stopPropagation)
6851          */
6852         stopEvent : function(){
6853             if(this.browserEvent){
6854                 if(this.browserEvent.type == 'mousedown'){
6855                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6856                 }
6857                 E.stopEvent(this.browserEvent);
6858             }
6859         },
6860
6861         /**
6862          * Prevents the browsers default handling of the event.
6863          */
6864         preventDefault : function(){
6865             if(this.browserEvent){
6866                 E.preventDefault(this.browserEvent);
6867             }
6868         },
6869
6870         /** @private */
6871         isNavKeyPress : function(){
6872             var k = this.keyCode;
6873             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6874             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6875         },
6876
6877         isSpecialKey : function(){
6878             var k = this.keyCode;
6879             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6880             (k == 16) || (k == 17) ||
6881             (k >= 18 && k <= 20) ||
6882             (k >= 33 && k <= 35) ||
6883             (k >= 36 && k <= 39) ||
6884             (k >= 44 && k <= 45);
6885         },
6886         /**
6887          * Cancels bubbling of the event.
6888          */
6889         stopPropagation : function(){
6890             if(this.browserEvent){
6891                 if(this.type == 'mousedown'){
6892                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6893                 }
6894                 E.stopPropagation(this.browserEvent);
6895             }
6896         },
6897
6898         /**
6899          * Gets the key code for the event.
6900          * @return {Number}
6901          */
6902         getCharCode : function(){
6903             return this.charCode || this.keyCode;
6904         },
6905
6906         /**
6907          * Returns a normalized keyCode for the event.
6908          * @return {Number} The key code
6909          */
6910         getKey : function(){
6911             var k = this.keyCode || this.charCode;
6912             return Roo.isSafari ? (safariKeys[k] || k) : k;
6913         },
6914
6915         /**
6916          * Gets the x coordinate of the event.
6917          * @return {Number}
6918          */
6919         getPageX : function(){
6920             return this.xy[0];
6921         },
6922
6923         /**
6924          * Gets the y coordinate of the event.
6925          * @return {Number}
6926          */
6927         getPageY : function(){
6928             return this.xy[1];
6929         },
6930
6931         /**
6932          * Gets the time of the event.
6933          * @return {Number}
6934          */
6935         getTime : function(){
6936             if(this.browserEvent){
6937                 return E.getTime(this.browserEvent);
6938             }
6939             return null;
6940         },
6941
6942         /**
6943          * Gets the page coordinates of the event.
6944          * @return {Array} The xy values like [x, y]
6945          */
6946         getXY : function(){
6947             return this.xy;
6948         },
6949
6950         /**
6951          * Gets the target for the event.
6952          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6953          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6954                 search as a number or element (defaults to 10 || document.body)
6955          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6956          * @return {HTMLelement}
6957          */
6958         getTarget : function(selector, maxDepth, returnEl){
6959             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6960         },
6961         /**
6962          * Gets the related target.
6963          * @return {HTMLElement}
6964          */
6965         getRelatedTarget : function(){
6966             if(this.browserEvent){
6967                 return E.getRelatedTarget(this.browserEvent);
6968             }
6969             return null;
6970         },
6971
6972         /**
6973          * Normalizes mouse wheel delta across browsers
6974          * @return {Number} The delta
6975          */
6976         getWheelDelta : function(){
6977             var e = this.browserEvent;
6978             var delta = 0;
6979             if(e.wheelDelta){ /* IE/Opera. */
6980                 delta = e.wheelDelta/120;
6981             }else if(e.detail){ /* Mozilla case. */
6982                 delta = -e.detail/3;
6983             }
6984             return delta;
6985         },
6986
6987         /**
6988          * Returns true if the control, meta, shift or alt key was pressed during this event.
6989          * @return {Boolean}
6990          */
6991         hasModifier : function(){
6992             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6993         },
6994
6995         /**
6996          * Returns true if the target of this event equals el or is a child of el
6997          * @param {String/HTMLElement/Element} el
6998          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6999          * @return {Boolean}
7000          */
7001         within : function(el, related){
7002             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7003             return t && Roo.fly(el).contains(t);
7004         },
7005
7006         getPoint : function(){
7007             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7008         }
7009     };
7010
7011     return new Roo.EventObjectImpl();
7012 }();
7013             
7014     /*
7015  * Based on:
7016  * Ext JS Library 1.1.1
7017  * Copyright(c) 2006-2007, Ext JS, LLC.
7018  *
7019  * Originally Released Under LGPL - original licence link has changed is not relivant.
7020  *
7021  * Fork - LGPL
7022  * <script type="text/javascript">
7023  */
7024
7025  
7026 // was in Composite Element!??!?!
7027  
7028 (function(){
7029     var D = Roo.lib.Dom;
7030     var E = Roo.lib.Event;
7031     var A = Roo.lib.Anim;
7032
7033     // local style camelizing for speed
7034     var propCache = {};
7035     var camelRe = /(-[a-z])/gi;
7036     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7037     var view = document.defaultView;
7038
7039 /**
7040  * @class Roo.Element
7041  * Represents an Element in the DOM.<br><br>
7042  * Usage:<br>
7043 <pre><code>
7044 var el = Roo.get("my-div");
7045
7046 // or with getEl
7047 var el = getEl("my-div");
7048
7049 // or with a DOM element
7050 var el = Roo.get(myDivElement);
7051 </code></pre>
7052  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7053  * each call instead of constructing a new one.<br><br>
7054  * <b>Animations</b><br />
7055  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7056  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7057 <pre>
7058 Option    Default   Description
7059 --------- --------  ---------------------------------------------
7060 duration  .35       The duration of the animation in seconds
7061 easing    easeOut   The YUI easing method
7062 callback  none      A function to execute when the anim completes
7063 scope     this      The scope (this) of the callback function
7064 </pre>
7065 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7066 * manipulate the animation. Here's an example:
7067 <pre><code>
7068 var el = Roo.get("my-div");
7069
7070 // no animation
7071 el.setWidth(100);
7072
7073 // default animation
7074 el.setWidth(100, true);
7075
7076 // animation with some options set
7077 el.setWidth(100, {
7078     duration: 1,
7079     callback: this.foo,
7080     scope: this
7081 });
7082
7083 // using the "anim" property to get the Anim object
7084 var opt = {
7085     duration: 1,
7086     callback: this.foo,
7087     scope: this
7088 };
7089 el.setWidth(100, opt);
7090 ...
7091 if(opt.anim.isAnimated()){
7092     opt.anim.stop();
7093 }
7094 </code></pre>
7095 * <b> Composite (Collections of) Elements</b><br />
7096  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7097  * @constructor Create a new Element directly.
7098  * @param {String/HTMLElement} element
7099  * @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).
7100  */
7101     Roo.Element = function(element, forceNew){
7102         var dom = typeof element == "string" ?
7103                 document.getElementById(element) : element;
7104         if(!dom){ // invalid id/element
7105             return null;
7106         }
7107         var id = dom.id;
7108         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7109             return Roo.Element.cache[id];
7110         }
7111
7112         /**
7113          * The DOM element
7114          * @type HTMLElement
7115          */
7116         this.dom = dom;
7117
7118         /**
7119          * The DOM element ID
7120          * @type String
7121          */
7122         this.id = id || Roo.id(dom);
7123     };
7124
7125     var El = Roo.Element;
7126
7127     El.prototype = {
7128         /**
7129          * The element's default display mode  (defaults to "")
7130          * @type String
7131          */
7132         originalDisplay : "",
7133
7134         visibilityMode : 1,
7135         /**
7136          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7137          * @type String
7138          */
7139         defaultUnit : "px",
7140         
7141         /**
7142          * Sets the element's visibility mode. When setVisible() is called it
7143          * will use this to determine whether to set the visibility or the display property.
7144          * @param visMode Element.VISIBILITY or Element.DISPLAY
7145          * @return {Roo.Element} this
7146          */
7147         setVisibilityMode : function(visMode){
7148             this.visibilityMode = visMode;
7149             return this;
7150         },
7151         /**
7152          * Convenience method for setVisibilityMode(Element.DISPLAY)
7153          * @param {String} display (optional) What to set display to when visible
7154          * @return {Roo.Element} this
7155          */
7156         enableDisplayMode : function(display){
7157             this.setVisibilityMode(El.DISPLAY);
7158             if(typeof display != "undefined") { this.originalDisplay = display; }
7159             return this;
7160         },
7161
7162         /**
7163          * 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)
7164          * @param {String} selector The simple selector to test
7165          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7166                 search as a number or element (defaults to 10 || document.body)
7167          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7168          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7169          */
7170         findParent : function(simpleSelector, maxDepth, returnEl){
7171             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7172             maxDepth = maxDepth || 50;
7173             if(typeof maxDepth != "number"){
7174                 stopEl = Roo.getDom(maxDepth);
7175                 maxDepth = 10;
7176             }
7177             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7178                 if(dq.is(p, simpleSelector)){
7179                     return returnEl ? Roo.get(p) : p;
7180                 }
7181                 depth++;
7182                 p = p.parentNode;
7183             }
7184             return null;
7185         },
7186
7187
7188         /**
7189          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7190          * @param {String} selector The simple selector to test
7191          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7192                 search as a number or element (defaults to 10 || document.body)
7193          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7194          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7195          */
7196         findParentNode : function(simpleSelector, maxDepth, returnEl){
7197             var p = Roo.fly(this.dom.parentNode, '_internal');
7198             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7199         },
7200         
7201         /**
7202          * Looks at  the scrollable parent element
7203          */
7204         findScrollableParent : function()
7205         {
7206             var overflowRegex = /(auto|scroll)/;
7207             
7208             if(this.getStyle('position') === 'fixed'){
7209                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7210             }
7211             
7212             var excludeStaticParent = this.getStyle('position') === "absolute";
7213             
7214             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7215                 
7216                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7217                     continue;
7218                 }
7219                 
7220                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7221                     return parent;
7222                 }
7223                 
7224                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7225                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7226                 }
7227             }
7228             
7229             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7230         },
7231
7232         /**
7233          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7234          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7235          * @param {String} selector The simple selector to test
7236          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7237                 search as a number or element (defaults to 10 || document.body)
7238          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7239          */
7240         up : function(simpleSelector, maxDepth){
7241             return this.findParentNode(simpleSelector, maxDepth, true);
7242         },
7243
7244
7245
7246         /**
7247          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7248          * @param {String} selector The simple selector to test
7249          * @return {Boolean} True if this element matches the selector, else false
7250          */
7251         is : function(simpleSelector){
7252             return Roo.DomQuery.is(this.dom, simpleSelector);
7253         },
7254
7255         /**
7256          * Perform animation on this element.
7257          * @param {Object} args The YUI animation control args
7258          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7259          * @param {Function} onComplete (optional) Function to call when animation completes
7260          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7261          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7262          * @return {Roo.Element} this
7263          */
7264         animate : function(args, duration, onComplete, easing, animType){
7265             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7266             return this;
7267         },
7268
7269         /*
7270          * @private Internal animation call
7271          */
7272         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7273             animType = animType || 'run';
7274             opt = opt || {};
7275             var anim = Roo.lib.Anim[animType](
7276                 this.dom, args,
7277                 (opt.duration || defaultDur) || .35,
7278                 (opt.easing || defaultEase) || 'easeOut',
7279                 function(){
7280                     Roo.callback(cb, this);
7281                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7282                 },
7283                 this
7284             );
7285             opt.anim = anim;
7286             return anim;
7287         },
7288
7289         // private legacy anim prep
7290         preanim : function(a, i){
7291             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7292         },
7293
7294         /**
7295          * Removes worthless text nodes
7296          * @param {Boolean} forceReclean (optional) By default the element
7297          * keeps track if it has been cleaned already so
7298          * you can call this over and over. However, if you update the element and
7299          * need to force a reclean, you can pass true.
7300          */
7301         clean : function(forceReclean){
7302             if(this.isCleaned && forceReclean !== true){
7303                 return this;
7304             }
7305             var ns = /\S/;
7306             var d = this.dom, n = d.firstChild, ni = -1;
7307             while(n){
7308                 var nx = n.nextSibling;
7309                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7310                     d.removeChild(n);
7311                 }else{
7312                     n.nodeIndex = ++ni;
7313                 }
7314                 n = nx;
7315             }
7316             this.isCleaned = true;
7317             return this;
7318         },
7319
7320         // private
7321         calcOffsetsTo : function(el){
7322             el = Roo.get(el);
7323             var d = el.dom;
7324             var restorePos = false;
7325             if(el.getStyle('position') == 'static'){
7326                 el.position('relative');
7327                 restorePos = true;
7328             }
7329             var x = 0, y =0;
7330             var op = this.dom;
7331             while(op && op != d && op.tagName != 'HTML'){
7332                 x+= op.offsetLeft;
7333                 y+= op.offsetTop;
7334                 op = op.offsetParent;
7335             }
7336             if(restorePos){
7337                 el.position('static');
7338             }
7339             return [x, y];
7340         },
7341
7342         /**
7343          * Scrolls this element into view within the passed container.
7344          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7345          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7346          * @return {Roo.Element} this
7347          */
7348         scrollIntoView : function(container, hscroll){
7349             var c = Roo.getDom(container) || document.body;
7350             var el = this.dom;
7351
7352             var o = this.calcOffsetsTo(c),
7353                 l = o[0],
7354                 t = o[1],
7355                 b = t+el.offsetHeight,
7356                 r = l+el.offsetWidth;
7357
7358             var ch = c.clientHeight;
7359             var ct = parseInt(c.scrollTop, 10);
7360             var cl = parseInt(c.scrollLeft, 10);
7361             var cb = ct + ch;
7362             var cr = cl + c.clientWidth;
7363
7364             if(t < ct){
7365                 c.scrollTop = t;
7366             }else if(b > cb){
7367                 c.scrollTop = b-ch;
7368             }
7369
7370             if(hscroll !== false){
7371                 if(l < cl){
7372                     c.scrollLeft = l;
7373                 }else if(r > cr){
7374                     c.scrollLeft = r-c.clientWidth;
7375                 }
7376             }
7377             return this;
7378         },
7379
7380         // private
7381         scrollChildIntoView : function(child, hscroll){
7382             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7383         },
7384
7385         /**
7386          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7387          * the new height may not be available immediately.
7388          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7389          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7390          * @param {Function} onComplete (optional) Function to call when animation completes
7391          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7392          * @return {Roo.Element} this
7393          */
7394         autoHeight : function(animate, duration, onComplete, easing){
7395             var oldHeight = this.getHeight();
7396             this.clip();
7397             this.setHeight(1); // force clipping
7398             setTimeout(function(){
7399                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7400                 if(!animate){
7401                     this.setHeight(height);
7402                     this.unclip();
7403                     if(typeof onComplete == "function"){
7404                         onComplete();
7405                     }
7406                 }else{
7407                     this.setHeight(oldHeight); // restore original height
7408                     this.setHeight(height, animate, duration, function(){
7409                         this.unclip();
7410                         if(typeof onComplete == "function") { onComplete(); }
7411                     }.createDelegate(this), easing);
7412                 }
7413             }.createDelegate(this), 0);
7414             return this;
7415         },
7416
7417         /**
7418          * Returns true if this element is an ancestor of the passed element
7419          * @param {HTMLElement/String} el The element to check
7420          * @return {Boolean} True if this element is an ancestor of el, else false
7421          */
7422         contains : function(el){
7423             if(!el){return false;}
7424             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7425         },
7426
7427         /**
7428          * Checks whether the element is currently visible using both visibility and display properties.
7429          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7430          * @return {Boolean} True if the element is currently visible, else false
7431          */
7432         isVisible : function(deep) {
7433             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7434             if(deep !== true || !vis){
7435                 return vis;
7436             }
7437             var p = this.dom.parentNode;
7438             while(p && p.tagName.toLowerCase() != "body"){
7439                 if(!Roo.fly(p, '_isVisible').isVisible()){
7440                     return false;
7441                 }
7442                 p = p.parentNode;
7443             }
7444             return true;
7445         },
7446
7447         /**
7448          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7449          * @param {String} selector The CSS selector
7450          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7451          * @return {CompositeElement/CompositeElementLite} The composite element
7452          */
7453         select : function(selector, unique){
7454             return El.select(selector, unique, this.dom);
7455         },
7456
7457         /**
7458          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7459          * @param {String} selector The CSS selector
7460          * @return {Array} An array of the matched nodes
7461          */
7462         query : function(selector, unique){
7463             return Roo.DomQuery.select(selector, this.dom);
7464         },
7465
7466         /**
7467          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7468          * @param {String} selector The CSS selector
7469          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7470          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7471          */
7472         child : function(selector, returnDom){
7473             var n = Roo.DomQuery.selectNode(selector, this.dom);
7474             return returnDom ? n : Roo.get(n);
7475         },
7476
7477         /**
7478          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7479          * @param {String} selector The CSS selector
7480          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7481          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7482          */
7483         down : function(selector, returnDom){
7484             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7485             return returnDom ? n : Roo.get(n);
7486         },
7487
7488         /**
7489          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7490          * @param {String} group The group the DD object is member of
7491          * @param {Object} config The DD config object
7492          * @param {Object} overrides An object containing methods to override/implement on the DD object
7493          * @return {Roo.dd.DD} The DD object
7494          */
7495         initDD : function(group, config, overrides){
7496             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7497             return Roo.apply(dd, overrides);
7498         },
7499
7500         /**
7501          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7502          * @param {String} group The group the DDProxy object is member of
7503          * @param {Object} config The DDProxy config object
7504          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7505          * @return {Roo.dd.DDProxy} The DDProxy object
7506          */
7507         initDDProxy : function(group, config, overrides){
7508             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7509             return Roo.apply(dd, overrides);
7510         },
7511
7512         /**
7513          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7514          * @param {String} group The group the DDTarget object is member of
7515          * @param {Object} config The DDTarget config object
7516          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7517          * @return {Roo.dd.DDTarget} The DDTarget object
7518          */
7519         initDDTarget : function(group, config, overrides){
7520             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7521             return Roo.apply(dd, overrides);
7522         },
7523
7524         /**
7525          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7526          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7527          * @param {Boolean} visible Whether the element is visible
7528          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7529          * @return {Roo.Element} this
7530          */
7531          setVisible : function(visible, animate){
7532             if(!animate || !A){
7533                 if(this.visibilityMode == El.DISPLAY){
7534                     this.setDisplayed(visible);
7535                 }else{
7536                     this.fixDisplay();
7537                     this.dom.style.visibility = visible ? "visible" : "hidden";
7538                 }
7539             }else{
7540                 // closure for composites
7541                 var dom = this.dom;
7542                 var visMode = this.visibilityMode;
7543                 if(visible){
7544                     this.setOpacity(.01);
7545                     this.setVisible(true);
7546                 }
7547                 this.anim({opacity: { to: (visible?1:0) }},
7548                       this.preanim(arguments, 1),
7549                       null, .35, 'easeIn', function(){
7550                          if(!visible){
7551                              if(visMode == El.DISPLAY){
7552                                  dom.style.display = "none";
7553                              }else{
7554                                  dom.style.visibility = "hidden";
7555                              }
7556                              Roo.get(dom).setOpacity(1);
7557                          }
7558                      });
7559             }
7560             return this;
7561         },
7562
7563         /**
7564          * Returns true if display is not "none"
7565          * @return {Boolean}
7566          */
7567         isDisplayed : function() {
7568             return this.getStyle("display") != "none";
7569         },
7570
7571         /**
7572          * Toggles the element's visibility or display, depending on visibility mode.
7573          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7574          * @return {Roo.Element} this
7575          */
7576         toggle : function(animate){
7577             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7578             return this;
7579         },
7580
7581         /**
7582          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7583          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7584          * @return {Roo.Element} this
7585          */
7586         setDisplayed : function(value) {
7587             if(typeof value == "boolean"){
7588                value = value ? this.originalDisplay : "none";
7589             }
7590             this.setStyle("display", value);
7591             return this;
7592         },
7593
7594         /**
7595          * Tries to focus the element. Any exceptions are caught and ignored.
7596          * @return {Roo.Element} this
7597          */
7598         focus : function() {
7599             try{
7600                 this.dom.focus();
7601             }catch(e){}
7602             return this;
7603         },
7604
7605         /**
7606          * Tries to blur the element. Any exceptions are caught and ignored.
7607          * @return {Roo.Element} this
7608          */
7609         blur : function() {
7610             try{
7611                 this.dom.blur();
7612             }catch(e){}
7613             return this;
7614         },
7615
7616         /**
7617          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7618          * @param {String/Array} className The CSS class to add, or an array of classes
7619          * @return {Roo.Element} this
7620          */
7621         addClass : function(className){
7622             if(className instanceof Array){
7623                 for(var i = 0, len = className.length; i < len; i++) {
7624                     this.addClass(className[i]);
7625                 }
7626             }else{
7627                 if(className && !this.hasClass(className)){
7628                     this.dom.className = this.dom.className + " " + className;
7629                 }
7630             }
7631             return this;
7632         },
7633
7634         /**
7635          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7636          * @param {String/Array} className The CSS class to add, or an array of classes
7637          * @return {Roo.Element} this
7638          */
7639         radioClass : function(className){
7640             var siblings = this.dom.parentNode.childNodes;
7641             for(var i = 0; i < siblings.length; i++) {
7642                 var s = siblings[i];
7643                 if(s.nodeType == 1){
7644                     Roo.get(s).removeClass(className);
7645                 }
7646             }
7647             this.addClass(className);
7648             return this;
7649         },
7650
7651         /**
7652          * Removes one or more CSS classes from the element.
7653          * @param {String/Array} className The CSS class to remove, or an array of classes
7654          * @return {Roo.Element} this
7655          */
7656         removeClass : function(className){
7657             if(!className || !this.dom.className){
7658                 return this;
7659             }
7660             if(className instanceof Array){
7661                 for(var i = 0, len = className.length; i < len; i++) {
7662                     this.removeClass(className[i]);
7663                 }
7664             }else{
7665                 if(this.hasClass(className)){
7666                     var re = this.classReCache[className];
7667                     if (!re) {
7668                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7669                        this.classReCache[className] = re;
7670                     }
7671                     this.dom.className =
7672                         this.dom.className.replace(re, " ");
7673                 }
7674             }
7675             return this;
7676         },
7677
7678         // private
7679         classReCache: {},
7680
7681         /**
7682          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7683          * @param {String} className The CSS class to toggle
7684          * @return {Roo.Element} this
7685          */
7686         toggleClass : function(className){
7687             if(this.hasClass(className)){
7688                 this.removeClass(className);
7689             }else{
7690                 this.addClass(className);
7691             }
7692             return this;
7693         },
7694
7695         /**
7696          * Checks if the specified CSS class exists on this element's DOM node.
7697          * @param {String} className The CSS class to check for
7698          * @return {Boolean} True if the class exists, else false
7699          */
7700         hasClass : function(className){
7701             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7702         },
7703
7704         /**
7705          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7706          * @param {String} oldClassName The CSS class to replace
7707          * @param {String} newClassName The replacement CSS class
7708          * @return {Roo.Element} this
7709          */
7710         replaceClass : function(oldClassName, newClassName){
7711             this.removeClass(oldClassName);
7712             this.addClass(newClassName);
7713             return this;
7714         },
7715
7716         /**
7717          * Returns an object with properties matching the styles requested.
7718          * For example, el.getStyles('color', 'font-size', 'width') might return
7719          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7720          * @param {String} style1 A style name
7721          * @param {String} style2 A style name
7722          * @param {String} etc.
7723          * @return {Object} The style object
7724          */
7725         getStyles : function(){
7726             var a = arguments, len = a.length, r = {};
7727             for(var i = 0; i < len; i++){
7728                 r[a[i]] = this.getStyle(a[i]);
7729             }
7730             return r;
7731         },
7732
7733         /**
7734          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7735          * @param {String} property The style property whose value is returned.
7736          * @return {String} The current value of the style property for this element.
7737          */
7738         getStyle : function(){
7739             return view && view.getComputedStyle ?
7740                 function(prop){
7741                     var el = this.dom, v, cs, camel;
7742                     if(prop == 'float'){
7743                         prop = "cssFloat";
7744                     }
7745                     if(el.style && (v = el.style[prop])){
7746                         return v;
7747                     }
7748                     if(cs = view.getComputedStyle(el, "")){
7749                         if(!(camel = propCache[prop])){
7750                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7751                         }
7752                         return cs[camel];
7753                     }
7754                     return null;
7755                 } :
7756                 function(prop){
7757                     var el = this.dom, v, cs, camel;
7758                     if(prop == 'opacity'){
7759                         if(typeof el.style.filter == 'string'){
7760                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7761                             if(m){
7762                                 var fv = parseFloat(m[1]);
7763                                 if(!isNaN(fv)){
7764                                     return fv ? fv / 100 : 0;
7765                                 }
7766                             }
7767                         }
7768                         return 1;
7769                     }else if(prop == 'float'){
7770                         prop = "styleFloat";
7771                     }
7772                     if(!(camel = propCache[prop])){
7773                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7774                     }
7775                     if(v = el.style[camel]){
7776                         return v;
7777                     }
7778                     if(cs = el.currentStyle){
7779                         return cs[camel];
7780                     }
7781                     return null;
7782                 };
7783         }(),
7784
7785         /**
7786          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7787          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7788          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7789          * @return {Roo.Element} this
7790          */
7791         setStyle : function(prop, value){
7792             if(typeof prop == "string"){
7793                 
7794                 if (prop == 'float') {
7795                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7796                     return this;
7797                 }
7798                 
7799                 var camel;
7800                 if(!(camel = propCache[prop])){
7801                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7802                 }
7803                 
7804                 if(camel == 'opacity') {
7805                     this.setOpacity(value);
7806                 }else{
7807                     this.dom.style[camel] = value;
7808                 }
7809             }else{
7810                 for(var style in prop){
7811                     if(typeof prop[style] != "function"){
7812                        this.setStyle(style, prop[style]);
7813                     }
7814                 }
7815             }
7816             return this;
7817         },
7818
7819         /**
7820          * More flexible version of {@link #setStyle} for setting style properties.
7821          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7822          * a function which returns such a specification.
7823          * @return {Roo.Element} this
7824          */
7825         applyStyles : function(style){
7826             Roo.DomHelper.applyStyles(this.dom, style);
7827             return this;
7828         },
7829
7830         /**
7831           * 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).
7832           * @return {Number} The X position of the element
7833           */
7834         getX : function(){
7835             return D.getX(this.dom);
7836         },
7837
7838         /**
7839           * 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).
7840           * @return {Number} The Y position of the element
7841           */
7842         getY : function(){
7843             return D.getY(this.dom);
7844         },
7845
7846         /**
7847           * 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).
7848           * @return {Array} The XY position of the element
7849           */
7850         getXY : function(){
7851             return D.getXY(this.dom);
7852         },
7853
7854         /**
7855          * 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).
7856          * @param {Number} The X position of the element
7857          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7858          * @return {Roo.Element} this
7859          */
7860         setX : function(x, animate){
7861             if(!animate || !A){
7862                 D.setX(this.dom, x);
7863             }else{
7864                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7865             }
7866             return this;
7867         },
7868
7869         /**
7870          * 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).
7871          * @param {Number} The Y position of the element
7872          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7873          * @return {Roo.Element} this
7874          */
7875         setY : function(y, animate){
7876             if(!animate || !A){
7877                 D.setY(this.dom, y);
7878             }else{
7879                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7880             }
7881             return this;
7882         },
7883
7884         /**
7885          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7886          * @param {String} left The left CSS property value
7887          * @return {Roo.Element} this
7888          */
7889         setLeft : function(left){
7890             this.setStyle("left", this.addUnits(left));
7891             return this;
7892         },
7893
7894         /**
7895          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7896          * @param {String} top The top CSS property value
7897          * @return {Roo.Element} this
7898          */
7899         setTop : function(top){
7900             this.setStyle("top", this.addUnits(top));
7901             return this;
7902         },
7903
7904         /**
7905          * Sets the element's CSS right style.
7906          * @param {String} right The right CSS property value
7907          * @return {Roo.Element} this
7908          */
7909         setRight : function(right){
7910             this.setStyle("right", this.addUnits(right));
7911             return this;
7912         },
7913
7914         /**
7915          * Sets the element's CSS bottom style.
7916          * @param {String} bottom The bottom CSS property value
7917          * @return {Roo.Element} this
7918          */
7919         setBottom : function(bottom){
7920             this.setStyle("bottom", this.addUnits(bottom));
7921             return this;
7922         },
7923
7924         /**
7925          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7926          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7927          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7928          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7929          * @return {Roo.Element} this
7930          */
7931         setXY : function(pos, animate){
7932             if(!animate || !A){
7933                 D.setXY(this.dom, pos);
7934             }else{
7935                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7936             }
7937             return this;
7938         },
7939
7940         /**
7941          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7942          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7943          * @param {Number} x X value for new position (coordinates are page-based)
7944          * @param {Number} y Y value for new position (coordinates are page-based)
7945          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7946          * @return {Roo.Element} this
7947          */
7948         setLocation : function(x, y, animate){
7949             this.setXY([x, y], this.preanim(arguments, 2));
7950             return this;
7951         },
7952
7953         /**
7954          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7955          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7956          * @param {Number} x X value for new position (coordinates are page-based)
7957          * @param {Number} y Y value for new position (coordinates are page-based)
7958          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7959          * @return {Roo.Element} this
7960          */
7961         moveTo : function(x, y, animate){
7962             this.setXY([x, y], this.preanim(arguments, 2));
7963             return this;
7964         },
7965
7966         /**
7967          * Returns the region of the given element.
7968          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7969          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7970          */
7971         getRegion : function(){
7972             return D.getRegion(this.dom);
7973         },
7974
7975         /**
7976          * Returns the offset height of the element
7977          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7978          * @return {Number} The element's height
7979          */
7980         getHeight : function(contentHeight){
7981             var h = this.dom.offsetHeight || 0;
7982             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7983         },
7984
7985         /**
7986          * Returns the offset width of the element
7987          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7988          * @return {Number} The element's width
7989          */
7990         getWidth : function(contentWidth){
7991             var w = this.dom.offsetWidth || 0;
7992             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7993         },
7994
7995         /**
7996          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7997          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7998          * if a height has not been set using CSS.
7999          * @return {Number}
8000          */
8001         getComputedHeight : function(){
8002             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8003             if(!h){
8004                 h = parseInt(this.getStyle('height'), 10) || 0;
8005                 if(!this.isBorderBox()){
8006                     h += this.getFrameWidth('tb');
8007                 }
8008             }
8009             return h;
8010         },
8011
8012         /**
8013          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8014          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8015          * if a width has not been set using CSS.
8016          * @return {Number}
8017          */
8018         getComputedWidth : function(){
8019             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8020             if(!w){
8021                 w = parseInt(this.getStyle('width'), 10) || 0;
8022                 if(!this.isBorderBox()){
8023                     w += this.getFrameWidth('lr');
8024                 }
8025             }
8026             return w;
8027         },
8028
8029         /**
8030          * Returns the size of the element.
8031          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8032          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8033          */
8034         getSize : function(contentSize){
8035             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8036         },
8037
8038         /**
8039          * Returns the width and height of the viewport.
8040          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8041          */
8042         getViewSize : function(){
8043             var d = this.dom, doc = document, aw = 0, ah = 0;
8044             if(d == doc || d == doc.body){
8045                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8046             }else{
8047                 return {
8048                     width : d.clientWidth,
8049                     height: d.clientHeight
8050                 };
8051             }
8052         },
8053
8054         /**
8055          * Returns the value of the "value" attribute
8056          * @param {Boolean} asNumber true to parse the value as a number
8057          * @return {String/Number}
8058          */
8059         getValue : function(asNumber){
8060             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8061         },
8062
8063         // private
8064         adjustWidth : function(width){
8065             if(typeof width == "number"){
8066                 if(this.autoBoxAdjust && !this.isBorderBox()){
8067                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8068                 }
8069                 if(width < 0){
8070                     width = 0;
8071                 }
8072             }
8073             return width;
8074         },
8075
8076         // private
8077         adjustHeight : function(height){
8078             if(typeof height == "number"){
8079                if(this.autoBoxAdjust && !this.isBorderBox()){
8080                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8081                }
8082                if(height < 0){
8083                    height = 0;
8084                }
8085             }
8086             return height;
8087         },
8088
8089         /**
8090          * Set the width of the element
8091          * @param {Number} width The new width
8092          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8093          * @return {Roo.Element} this
8094          */
8095         setWidth : function(width, animate){
8096             width = this.adjustWidth(width);
8097             if(!animate || !A){
8098                 this.dom.style.width = this.addUnits(width);
8099             }else{
8100                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8101             }
8102             return this;
8103         },
8104
8105         /**
8106          * Set the height of the element
8107          * @param {Number} height The new height
8108          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8109          * @return {Roo.Element} this
8110          */
8111          setHeight : function(height, animate){
8112             height = this.adjustHeight(height);
8113             if(!animate || !A){
8114                 this.dom.style.height = this.addUnits(height);
8115             }else{
8116                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8117             }
8118             return this;
8119         },
8120
8121         /**
8122          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8123          * @param {Number} width The new width
8124          * @param {Number} height The new height
8125          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8126          * @return {Roo.Element} this
8127          */
8128          setSize : function(width, height, animate){
8129             if(typeof width == "object"){ // in case of object from getSize()
8130                 height = width.height; width = width.width;
8131             }
8132             width = this.adjustWidth(width); height = this.adjustHeight(height);
8133             if(!animate || !A){
8134                 this.dom.style.width = this.addUnits(width);
8135                 this.dom.style.height = this.addUnits(height);
8136             }else{
8137                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8138             }
8139             return this;
8140         },
8141
8142         /**
8143          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8144          * @param {Number} x X value for new position (coordinates are page-based)
8145          * @param {Number} y Y value for new position (coordinates are page-based)
8146          * @param {Number} width The new width
8147          * @param {Number} height The new height
8148          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8149          * @return {Roo.Element} this
8150          */
8151         setBounds : function(x, y, width, height, animate){
8152             if(!animate || !A){
8153                 this.setSize(width, height);
8154                 this.setLocation(x, y);
8155             }else{
8156                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8157                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8158                               this.preanim(arguments, 4), 'motion');
8159             }
8160             return this;
8161         },
8162
8163         /**
8164          * 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.
8165          * @param {Roo.lib.Region} region The region to fill
8166          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8167          * @return {Roo.Element} this
8168          */
8169         setRegion : function(region, animate){
8170             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8171             return this;
8172         },
8173
8174         /**
8175          * Appends an event handler
8176          *
8177          * @param {String}   eventName     The type of event to append
8178          * @param {Function} fn        The method the event invokes
8179          * @param {Object} scope       (optional) The scope (this object) of the fn
8180          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8181          */
8182         addListener : function(eventName, fn, scope, options){
8183             if (this.dom) {
8184                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8185             }
8186         },
8187
8188         /**
8189          * Removes an event handler from this element
8190          * @param {String} eventName the type of event to remove
8191          * @param {Function} fn the method the event invokes
8192          * @return {Roo.Element} this
8193          */
8194         removeListener : function(eventName, fn){
8195             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8196             return this;
8197         },
8198
8199         /**
8200          * Removes all previous added listeners from this element
8201          * @return {Roo.Element} this
8202          */
8203         removeAllListeners : function(){
8204             E.purgeElement(this.dom);
8205             return this;
8206         },
8207
8208         relayEvent : function(eventName, observable){
8209             this.on(eventName, function(e){
8210                 observable.fireEvent(eventName, e);
8211             });
8212         },
8213
8214         /**
8215          * Set the opacity of the element
8216          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8217          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8218          * @return {Roo.Element} this
8219          */
8220          setOpacity : function(opacity, animate){
8221             if(!animate || !A){
8222                 var s = this.dom.style;
8223                 if(Roo.isIE){
8224                     s.zoom = 1;
8225                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8226                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8227                 }else{
8228                     s.opacity = opacity;
8229                 }
8230             }else{
8231                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8232             }
8233             return this;
8234         },
8235
8236         /**
8237          * Gets the left X coordinate
8238          * @param {Boolean} local True to get the local css position instead of page coordinate
8239          * @return {Number}
8240          */
8241         getLeft : function(local){
8242             if(!local){
8243                 return this.getX();
8244             }else{
8245                 return parseInt(this.getStyle("left"), 10) || 0;
8246             }
8247         },
8248
8249         /**
8250          * Gets the right X coordinate of the element (element X position + element width)
8251          * @param {Boolean} local True to get the local css position instead of page coordinate
8252          * @return {Number}
8253          */
8254         getRight : function(local){
8255             if(!local){
8256                 return this.getX() + this.getWidth();
8257             }else{
8258                 return (this.getLeft(true) + this.getWidth()) || 0;
8259             }
8260         },
8261
8262         /**
8263          * Gets the top Y coordinate
8264          * @param {Boolean} local True to get the local css position instead of page coordinate
8265          * @return {Number}
8266          */
8267         getTop : function(local) {
8268             if(!local){
8269                 return this.getY();
8270             }else{
8271                 return parseInt(this.getStyle("top"), 10) || 0;
8272             }
8273         },
8274
8275         /**
8276          * Gets the bottom Y coordinate of the element (element Y position + element height)
8277          * @param {Boolean} local True to get the local css position instead of page coordinate
8278          * @return {Number}
8279          */
8280         getBottom : function(local){
8281             if(!local){
8282                 return this.getY() + this.getHeight();
8283             }else{
8284                 return (this.getTop(true) + this.getHeight()) || 0;
8285             }
8286         },
8287
8288         /**
8289         * Initializes positioning on this element. If a desired position is not passed, it will make the
8290         * the element positioned relative IF it is not already positioned.
8291         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8292         * @param {Number} zIndex (optional) The zIndex to apply
8293         * @param {Number} x (optional) Set the page X position
8294         * @param {Number} y (optional) Set the page Y position
8295         */
8296         position : function(pos, zIndex, x, y){
8297             if(!pos){
8298                if(this.getStyle('position') == 'static'){
8299                    this.setStyle('position', 'relative');
8300                }
8301             }else{
8302                 this.setStyle("position", pos);
8303             }
8304             if(zIndex){
8305                 this.setStyle("z-index", zIndex);
8306             }
8307             if(x !== undefined && y !== undefined){
8308                 this.setXY([x, y]);
8309             }else if(x !== undefined){
8310                 this.setX(x);
8311             }else if(y !== undefined){
8312                 this.setY(y);
8313             }
8314         },
8315
8316         /**
8317         * Clear positioning back to the default when the document was loaded
8318         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8319         * @return {Roo.Element} this
8320          */
8321         clearPositioning : function(value){
8322             value = value ||'';
8323             this.setStyle({
8324                 "left": value,
8325                 "right": value,
8326                 "top": value,
8327                 "bottom": value,
8328                 "z-index": "",
8329                 "position" : "static"
8330             });
8331             return this;
8332         },
8333
8334         /**
8335         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8336         * snapshot before performing an update and then restoring the element.
8337         * @return {Object}
8338         */
8339         getPositioning : function(){
8340             var l = this.getStyle("left");
8341             var t = this.getStyle("top");
8342             return {
8343                 "position" : this.getStyle("position"),
8344                 "left" : l,
8345                 "right" : l ? "" : this.getStyle("right"),
8346                 "top" : t,
8347                 "bottom" : t ? "" : this.getStyle("bottom"),
8348                 "z-index" : this.getStyle("z-index")
8349             };
8350         },
8351
8352         /**
8353          * Gets the width of the border(s) for the specified side(s)
8354          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8355          * passing lr would get the border (l)eft width + the border (r)ight width.
8356          * @return {Number} The width of the sides passed added together
8357          */
8358         getBorderWidth : function(side){
8359             return this.addStyles(side, El.borders);
8360         },
8361
8362         /**
8363          * Gets the width of the padding(s) for the specified side(s)
8364          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8365          * passing lr would get the padding (l)eft + the padding (r)ight.
8366          * @return {Number} The padding of the sides passed added together
8367          */
8368         getPadding : function(side){
8369             return this.addStyles(side, El.paddings);
8370         },
8371
8372         /**
8373         * Set positioning with an object returned by getPositioning().
8374         * @param {Object} posCfg
8375         * @return {Roo.Element} this
8376          */
8377         setPositioning : function(pc){
8378             this.applyStyles(pc);
8379             if(pc.right == "auto"){
8380                 this.dom.style.right = "";
8381             }
8382             if(pc.bottom == "auto"){
8383                 this.dom.style.bottom = "";
8384             }
8385             return this;
8386         },
8387
8388         // private
8389         fixDisplay : function(){
8390             if(this.getStyle("display") == "none"){
8391                 this.setStyle("visibility", "hidden");
8392                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8393                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8394                     this.setStyle("display", "block");
8395                 }
8396             }
8397         },
8398
8399         /**
8400          * Quick set left and top adding default units
8401          * @param {String} left The left CSS property value
8402          * @param {String} top The top CSS property value
8403          * @return {Roo.Element} this
8404          */
8405          setLeftTop : function(left, top){
8406             this.dom.style.left = this.addUnits(left);
8407             this.dom.style.top = this.addUnits(top);
8408             return this;
8409         },
8410
8411         /**
8412          * Move this element relative to its current position.
8413          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8414          * @param {Number} distance How far to move the element in pixels
8415          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8416          * @return {Roo.Element} this
8417          */
8418          move : function(direction, distance, animate){
8419             var xy = this.getXY();
8420             direction = direction.toLowerCase();
8421             switch(direction){
8422                 case "l":
8423                 case "left":
8424                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8425                     break;
8426                case "r":
8427                case "right":
8428                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8429                     break;
8430                case "t":
8431                case "top":
8432                case "up":
8433                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8434                     break;
8435                case "b":
8436                case "bottom":
8437                case "down":
8438                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8439                     break;
8440             }
8441             return this;
8442         },
8443
8444         /**
8445          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8446          * @return {Roo.Element} this
8447          */
8448         clip : function(){
8449             if(!this.isClipped){
8450                this.isClipped = true;
8451                this.originalClip = {
8452                    "o": this.getStyle("overflow"),
8453                    "x": this.getStyle("overflow-x"),
8454                    "y": this.getStyle("overflow-y")
8455                };
8456                this.setStyle("overflow", "hidden");
8457                this.setStyle("overflow-x", "hidden");
8458                this.setStyle("overflow-y", "hidden");
8459             }
8460             return this;
8461         },
8462
8463         /**
8464          *  Return clipping (overflow) to original clipping before clip() was called
8465          * @return {Roo.Element} this
8466          */
8467         unclip : function(){
8468             if(this.isClipped){
8469                 this.isClipped = false;
8470                 var o = this.originalClip;
8471                 if(o.o){this.setStyle("overflow", o.o);}
8472                 if(o.x){this.setStyle("overflow-x", o.x);}
8473                 if(o.y){this.setStyle("overflow-y", o.y);}
8474             }
8475             return this;
8476         },
8477
8478
8479         /**
8480          * Gets the x,y coordinates specified by the anchor position on the element.
8481          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8482          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8483          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8484          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8485          * @return {Array} [x, y] An array containing the element's x and y coordinates
8486          */
8487         getAnchorXY : function(anchor, local, s){
8488             //Passing a different size is useful for pre-calculating anchors,
8489             //especially for anchored animations that change the el size.
8490
8491             var w, h, vp = false;
8492             if(!s){
8493                 var d = this.dom;
8494                 if(d == document.body || d == document){
8495                     vp = true;
8496                     w = D.getViewWidth(); h = D.getViewHeight();
8497                 }else{
8498                     w = this.getWidth(); h = this.getHeight();
8499                 }
8500             }else{
8501                 w = s.width;  h = s.height;
8502             }
8503             var x = 0, y = 0, r = Math.round;
8504             switch((anchor || "tl").toLowerCase()){
8505                 case "c":
8506                     x = r(w*.5);
8507                     y = r(h*.5);
8508                 break;
8509                 case "t":
8510                     x = r(w*.5);
8511                     y = 0;
8512                 break;
8513                 case "l":
8514                     x = 0;
8515                     y = r(h*.5);
8516                 break;
8517                 case "r":
8518                     x = w;
8519                     y = r(h*.5);
8520                 break;
8521                 case "b":
8522                     x = r(w*.5);
8523                     y = h;
8524                 break;
8525                 case "tl":
8526                     x = 0;
8527                     y = 0;
8528                 break;
8529                 case "bl":
8530                     x = 0;
8531                     y = h;
8532                 break;
8533                 case "br":
8534                     x = w;
8535                     y = h;
8536                 break;
8537                 case "tr":
8538                     x = w;
8539                     y = 0;
8540                 break;
8541             }
8542             if(local === true){
8543                 return [x, y];
8544             }
8545             if(vp){
8546                 var sc = this.getScroll();
8547                 return [x + sc.left, y + sc.top];
8548             }
8549             //Add the element's offset xy
8550             var o = this.getXY();
8551             return [x+o[0], y+o[1]];
8552         },
8553
8554         /**
8555          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8556          * supported position values.
8557          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8558          * @param {String} position The position to align to.
8559          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8560          * @return {Array} [x, y]
8561          */
8562         getAlignToXY : function(el, p, o){
8563             el = Roo.get(el);
8564             var d = this.dom;
8565             if(!el.dom){
8566                 throw "Element.alignTo with an element that doesn't exist";
8567             }
8568             var c = false; //constrain to viewport
8569             var p1 = "", p2 = "";
8570             o = o || [0,0];
8571
8572             if(!p){
8573                 p = "tl-bl";
8574             }else if(p == "?"){
8575                 p = "tl-bl?";
8576             }else if(p.indexOf("-") == -1){
8577                 p = "tl-" + p;
8578             }
8579             p = p.toLowerCase();
8580             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8581             if(!m){
8582                throw "Element.alignTo with an invalid alignment " + p;
8583             }
8584             p1 = m[1]; p2 = m[2]; c = !!m[3];
8585
8586             //Subtract the aligned el's internal xy from the target's offset xy
8587             //plus custom offset to get the aligned el's new offset xy
8588             var a1 = this.getAnchorXY(p1, true);
8589             var a2 = el.getAnchorXY(p2, false);
8590             var x = a2[0] - a1[0] + o[0];
8591             var y = a2[1] - a1[1] + o[1];
8592             if(c){
8593                 //constrain the aligned el to viewport if necessary
8594                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8595                 // 5px of margin for ie
8596                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8597
8598                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8599                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8600                 //otherwise swap the aligned el to the opposite border of the target.
8601                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8602                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8603                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8604                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8605
8606                var doc = document;
8607                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8608                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8609
8610                if((x+w) > dw + scrollX){
8611                     x = swapX ? r.left-w : dw+scrollX-w;
8612                 }
8613                if(x < scrollX){
8614                    x = swapX ? r.right : scrollX;
8615                }
8616                if((y+h) > dh + scrollY){
8617                     y = swapY ? r.top-h : dh+scrollY-h;
8618                 }
8619                if (y < scrollY){
8620                    y = swapY ? r.bottom : scrollY;
8621                }
8622             }
8623             return [x,y];
8624         },
8625
8626         // private
8627         getConstrainToXY : function(){
8628             var os = {top:0, left:0, bottom:0, right: 0};
8629
8630             return function(el, local, offsets, proposedXY){
8631                 el = Roo.get(el);
8632                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8633
8634                 var vw, vh, vx = 0, vy = 0;
8635                 if(el.dom == document.body || el.dom == document){
8636                     vw = Roo.lib.Dom.getViewWidth();
8637                     vh = Roo.lib.Dom.getViewHeight();
8638                 }else{
8639                     vw = el.dom.clientWidth;
8640                     vh = el.dom.clientHeight;
8641                     if(!local){
8642                         var vxy = el.getXY();
8643                         vx = vxy[0];
8644                         vy = vxy[1];
8645                     }
8646                 }
8647
8648                 var s = el.getScroll();
8649
8650                 vx += offsets.left + s.left;
8651                 vy += offsets.top + s.top;
8652
8653                 vw -= offsets.right;
8654                 vh -= offsets.bottom;
8655
8656                 var vr = vx+vw;
8657                 var vb = vy+vh;
8658
8659                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8660                 var x = xy[0], y = xy[1];
8661                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8662
8663                 // only move it if it needs it
8664                 var moved = false;
8665
8666                 // first validate right/bottom
8667                 if((x + w) > vr){
8668                     x = vr - w;
8669                     moved = true;
8670                 }
8671                 if((y + h) > vb){
8672                     y = vb - h;
8673                     moved = true;
8674                 }
8675                 // then make sure top/left isn't negative
8676                 if(x < vx){
8677                     x = vx;
8678                     moved = true;
8679                 }
8680                 if(y < vy){
8681                     y = vy;
8682                     moved = true;
8683                 }
8684                 return moved ? [x, y] : false;
8685             };
8686         }(),
8687
8688         // private
8689         adjustForConstraints : function(xy, parent, offsets){
8690             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8691         },
8692
8693         /**
8694          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8695          * document it aligns it to the viewport.
8696          * The position parameter is optional, and can be specified in any one of the following formats:
8697          * <ul>
8698          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8699          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8700          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8701          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8702          *   <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
8703          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8704          * </ul>
8705          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8706          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8707          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8708          * that specified in order to enforce the viewport constraints.
8709          * Following are all of the supported anchor positions:
8710     <pre>
8711     Value  Description
8712     -----  -----------------------------
8713     tl     The top left corner (default)
8714     t      The center of the top edge
8715     tr     The top right corner
8716     l      The center of the left edge
8717     c      In the center of the element
8718     r      The center of the right edge
8719     bl     The bottom left corner
8720     b      The center of the bottom edge
8721     br     The bottom right corner
8722     </pre>
8723     Example Usage:
8724     <pre><code>
8725     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8726     el.alignTo("other-el");
8727
8728     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8729     el.alignTo("other-el", "tr?");
8730
8731     // align the bottom right corner of el with the center left edge of other-el
8732     el.alignTo("other-el", "br-l?");
8733
8734     // align the center of el with the bottom left corner of other-el and
8735     // adjust the x position by -6 pixels (and the y position by 0)
8736     el.alignTo("other-el", "c-bl", [-6, 0]);
8737     </code></pre>
8738          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8739          * @param {String} position The position to align to.
8740          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8741          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8742          * @return {Roo.Element} this
8743          */
8744         alignTo : function(element, position, offsets, animate){
8745             var xy = this.getAlignToXY(element, position, offsets);
8746             this.setXY(xy, this.preanim(arguments, 3));
8747             return this;
8748         },
8749
8750         /**
8751          * Anchors an element to another element and realigns it when the window is resized.
8752          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8753          * @param {String} position The position to align to.
8754          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8755          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8756          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8757          * is a number, it is used as the buffer delay (defaults to 50ms).
8758          * @param {Function} callback The function to call after the animation finishes
8759          * @return {Roo.Element} this
8760          */
8761         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8762             var action = function(){
8763                 this.alignTo(el, alignment, offsets, animate);
8764                 Roo.callback(callback, this);
8765             };
8766             Roo.EventManager.onWindowResize(action, this);
8767             var tm = typeof monitorScroll;
8768             if(tm != 'undefined'){
8769                 Roo.EventManager.on(window, 'scroll', action, this,
8770                     {buffer: tm == 'number' ? monitorScroll : 50});
8771             }
8772             action.call(this); // align immediately
8773             return this;
8774         },
8775         /**
8776          * Clears any opacity settings from this element. Required in some cases for IE.
8777          * @return {Roo.Element} this
8778          */
8779         clearOpacity : function(){
8780             if (window.ActiveXObject) {
8781                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8782                     this.dom.style.filter = "";
8783                 }
8784             } else {
8785                 this.dom.style.opacity = "";
8786                 this.dom.style["-moz-opacity"] = "";
8787                 this.dom.style["-khtml-opacity"] = "";
8788             }
8789             return this;
8790         },
8791
8792         /**
8793          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8794          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8795          * @return {Roo.Element} this
8796          */
8797         hide : function(animate){
8798             this.setVisible(false, this.preanim(arguments, 0));
8799             return this;
8800         },
8801
8802         /**
8803         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8804         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8805          * @return {Roo.Element} this
8806          */
8807         show : function(animate){
8808             this.setVisible(true, this.preanim(arguments, 0));
8809             return this;
8810         },
8811
8812         /**
8813          * @private Test if size has a unit, otherwise appends the default
8814          */
8815         addUnits : function(size){
8816             return Roo.Element.addUnits(size, this.defaultUnit);
8817         },
8818
8819         /**
8820          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8821          * @return {Roo.Element} this
8822          */
8823         beginMeasure : function(){
8824             var el = this.dom;
8825             if(el.offsetWidth || el.offsetHeight){
8826                 return this; // offsets work already
8827             }
8828             var changed = [];
8829             var p = this.dom, b = document.body; // start with this element
8830             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8831                 var pe = Roo.get(p);
8832                 if(pe.getStyle('display') == 'none'){
8833                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8834                     p.style.visibility = "hidden";
8835                     p.style.display = "block";
8836                 }
8837                 p = p.parentNode;
8838             }
8839             this._measureChanged = changed;
8840             return this;
8841
8842         },
8843
8844         /**
8845          * Restores displays to before beginMeasure was called
8846          * @return {Roo.Element} this
8847          */
8848         endMeasure : function(){
8849             var changed = this._measureChanged;
8850             if(changed){
8851                 for(var i = 0, len = changed.length; i < len; i++) {
8852                     var r = changed[i];
8853                     r.el.style.visibility = r.visibility;
8854                     r.el.style.display = "none";
8855                 }
8856                 this._measureChanged = null;
8857             }
8858             return this;
8859         },
8860
8861         /**
8862         * Update the innerHTML of this element, optionally searching for and processing scripts
8863         * @param {String} html The new HTML
8864         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8865         * @param {Function} callback For async script loading you can be noticed when the update completes
8866         * @return {Roo.Element} this
8867          */
8868         update : function(html, loadScripts, callback){
8869             if(typeof html == "undefined"){
8870                 html = "";
8871             }
8872             if(loadScripts !== true){
8873                 this.dom.innerHTML = html;
8874                 if(typeof callback == "function"){
8875                     callback();
8876                 }
8877                 return this;
8878             }
8879             var id = Roo.id();
8880             var dom = this.dom;
8881
8882             html += '<span id="' + id + '"></span>';
8883
8884             E.onAvailable(id, function(){
8885                 var hd = document.getElementsByTagName("head")[0];
8886                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8887                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8888                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8889
8890                 var match;
8891                 while(match = re.exec(html)){
8892                     var attrs = match[1];
8893                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8894                     if(srcMatch && srcMatch[2]){
8895                        var s = document.createElement("script");
8896                        s.src = srcMatch[2];
8897                        var typeMatch = attrs.match(typeRe);
8898                        if(typeMatch && typeMatch[2]){
8899                            s.type = typeMatch[2];
8900                        }
8901                        hd.appendChild(s);
8902                     }else if(match[2] && match[2].length > 0){
8903                         if(window.execScript) {
8904                            window.execScript(match[2]);
8905                         } else {
8906                             /**
8907                              * eval:var:id
8908                              * eval:var:dom
8909                              * eval:var:html
8910                              * 
8911                              */
8912                            window.eval(match[2]);
8913                         }
8914                     }
8915                 }
8916                 var el = document.getElementById(id);
8917                 if(el){el.parentNode.removeChild(el);}
8918                 if(typeof callback == "function"){
8919                     callback();
8920                 }
8921             });
8922             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8923             return this;
8924         },
8925
8926         /**
8927          * Direct access to the UpdateManager update() method (takes the same parameters).
8928          * @param {String/Function} url The url for this request or a function to call to get the url
8929          * @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}
8930          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8931          * @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.
8932          * @return {Roo.Element} this
8933          */
8934         load : function(){
8935             var um = this.getUpdateManager();
8936             um.update.apply(um, arguments);
8937             return this;
8938         },
8939
8940         /**
8941         * Gets this element's UpdateManager
8942         * @return {Roo.UpdateManager} The UpdateManager
8943         */
8944         getUpdateManager : function(){
8945             if(!this.updateManager){
8946                 this.updateManager = new Roo.UpdateManager(this);
8947             }
8948             return this.updateManager;
8949         },
8950
8951         /**
8952          * Disables text selection for this element (normalized across browsers)
8953          * @return {Roo.Element} this
8954          */
8955         unselectable : function(){
8956             this.dom.unselectable = "on";
8957             this.swallowEvent("selectstart", true);
8958             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8959             this.addClass("x-unselectable");
8960             return this;
8961         },
8962
8963         /**
8964         * Calculates the x, y to center this element on the screen
8965         * @return {Array} The x, y values [x, y]
8966         */
8967         getCenterXY : function(){
8968             return this.getAlignToXY(document, 'c-c');
8969         },
8970
8971         /**
8972         * Centers the Element in either the viewport, or another Element.
8973         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8974         */
8975         center : function(centerIn){
8976             this.alignTo(centerIn || document, 'c-c');
8977             return this;
8978         },
8979
8980         /**
8981          * Tests various css rules/browsers to determine if this element uses a border box
8982          * @return {Boolean}
8983          */
8984         isBorderBox : function(){
8985             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8986         },
8987
8988         /**
8989          * Return a box {x, y, width, height} that can be used to set another elements
8990          * size/location to match this element.
8991          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8992          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8993          * @return {Object} box An object in the format {x, y, width, height}
8994          */
8995         getBox : function(contentBox, local){
8996             var xy;
8997             if(!local){
8998                 xy = this.getXY();
8999             }else{
9000                 var left = parseInt(this.getStyle("left"), 10) || 0;
9001                 var top = parseInt(this.getStyle("top"), 10) || 0;
9002                 xy = [left, top];
9003             }
9004             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9005             if(!contentBox){
9006                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9007             }else{
9008                 var l = this.getBorderWidth("l")+this.getPadding("l");
9009                 var r = this.getBorderWidth("r")+this.getPadding("r");
9010                 var t = this.getBorderWidth("t")+this.getPadding("t");
9011                 var b = this.getBorderWidth("b")+this.getPadding("b");
9012                 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)};
9013             }
9014             bx.right = bx.x + bx.width;
9015             bx.bottom = bx.y + bx.height;
9016             return bx;
9017         },
9018
9019         /**
9020          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9021          for more information about the sides.
9022          * @param {String} sides
9023          * @return {Number}
9024          */
9025         getFrameWidth : function(sides, onlyContentBox){
9026             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9027         },
9028
9029         /**
9030          * 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.
9031          * @param {Object} box The box to fill {x, y, width, height}
9032          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9033          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9034          * @return {Roo.Element} this
9035          */
9036         setBox : function(box, adjust, animate){
9037             var w = box.width, h = box.height;
9038             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9039                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9040                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9041             }
9042             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9043             return this;
9044         },
9045
9046         /**
9047          * Forces the browser to repaint this element
9048          * @return {Roo.Element} this
9049          */
9050          repaint : function(){
9051             var dom = this.dom;
9052             this.addClass("x-repaint");
9053             setTimeout(function(){
9054                 Roo.get(dom).removeClass("x-repaint");
9055             }, 1);
9056             return this;
9057         },
9058
9059         /**
9060          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9061          * then it returns the calculated width of the sides (see getPadding)
9062          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9063          * @return {Object/Number}
9064          */
9065         getMargins : function(side){
9066             if(!side){
9067                 return {
9068                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9069                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9070                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9071                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9072                 };
9073             }else{
9074                 return this.addStyles(side, El.margins);
9075              }
9076         },
9077
9078         // private
9079         addStyles : function(sides, styles){
9080             var val = 0, v, w;
9081             for(var i = 0, len = sides.length; i < len; i++){
9082                 v = this.getStyle(styles[sides.charAt(i)]);
9083                 if(v){
9084                      w = parseInt(v, 10);
9085                      if(w){ val += w; }
9086                 }
9087             }
9088             return val;
9089         },
9090
9091         /**
9092          * Creates a proxy element of this element
9093          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9094          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9095          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9096          * @return {Roo.Element} The new proxy element
9097          */
9098         createProxy : function(config, renderTo, matchBox){
9099             if(renderTo){
9100                 renderTo = Roo.getDom(renderTo);
9101             }else{
9102                 renderTo = document.body;
9103             }
9104             config = typeof config == "object" ?
9105                 config : {tag : "div", cls: config};
9106             var proxy = Roo.DomHelper.append(renderTo, config, true);
9107             if(matchBox){
9108                proxy.setBox(this.getBox());
9109             }
9110             return proxy;
9111         },
9112
9113         /**
9114          * Puts a mask over this element to disable user interaction. Requires core.css.
9115          * This method can only be applied to elements which accept child nodes.
9116          * @param {String} msg (optional) A message to display in the mask
9117          * @param {String} msgCls (optional) A css class to apply to the msg element
9118          * @return {Element} The mask  element
9119          */
9120         mask : function(msg, msgCls)
9121         {
9122             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9123                 this.setStyle("position", "relative");
9124             }
9125             if(!this._mask){
9126                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9127             }
9128             
9129             this.addClass("x-masked");
9130             this._mask.setDisplayed(true);
9131             
9132             // we wander
9133             var z = 0;
9134             var dom = this.dom;
9135             while (dom && dom.style) {
9136                 if (!isNaN(parseInt(dom.style.zIndex))) {
9137                     z = Math.max(z, parseInt(dom.style.zIndex));
9138                 }
9139                 dom = dom.parentNode;
9140             }
9141             // if we are masking the body - then it hides everything..
9142             if (this.dom == document.body) {
9143                 z = 1000000;
9144                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9145                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9146             }
9147            
9148             if(typeof msg == 'string'){
9149                 if(!this._maskMsg){
9150                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9151                         cls: "roo-el-mask-msg", 
9152                         cn: [
9153                             {
9154                                 tag: 'i',
9155                                 cls: 'fa fa-spinner fa-spin'
9156                             },
9157                             {
9158                                 tag: 'div'
9159                             }   
9160                         ]
9161                     }, true);
9162                 }
9163                 var mm = this._maskMsg;
9164                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9165                 if (mm.dom.lastChild) { // weird IE issue?
9166                     mm.dom.lastChild.innerHTML = msg;
9167                 }
9168                 mm.setDisplayed(true);
9169                 mm.center(this);
9170                 mm.setStyle('z-index', z + 102);
9171             }
9172             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9173                 this._mask.setHeight(this.getHeight());
9174             }
9175             this._mask.setStyle('z-index', z + 100);
9176             
9177             return this._mask;
9178         },
9179
9180         /**
9181          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9182          * it is cached for reuse.
9183          */
9184         unmask : function(removeEl){
9185             if(this._mask){
9186                 if(removeEl === true){
9187                     this._mask.remove();
9188                     delete this._mask;
9189                     if(this._maskMsg){
9190                         this._maskMsg.remove();
9191                         delete this._maskMsg;
9192                     }
9193                 }else{
9194                     this._mask.setDisplayed(false);
9195                     if(this._maskMsg){
9196                         this._maskMsg.setDisplayed(false);
9197                     }
9198                 }
9199             }
9200             this.removeClass("x-masked");
9201         },
9202
9203         /**
9204          * Returns true if this element is masked
9205          * @return {Boolean}
9206          */
9207         isMasked : function(){
9208             return this._mask && this._mask.isVisible();
9209         },
9210
9211         /**
9212          * Creates an iframe shim for this element to keep selects and other windowed objects from
9213          * showing through.
9214          * @return {Roo.Element} The new shim element
9215          */
9216         createShim : function(){
9217             var el = document.createElement('iframe');
9218             el.frameBorder = 'no';
9219             el.className = 'roo-shim';
9220             if(Roo.isIE && Roo.isSecure){
9221                 el.src = Roo.SSL_SECURE_URL;
9222             }
9223             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9224             shim.autoBoxAdjust = false;
9225             return shim;
9226         },
9227
9228         /**
9229          * Removes this element from the DOM and deletes it from the cache
9230          */
9231         remove : function(){
9232             if(this.dom.parentNode){
9233                 this.dom.parentNode.removeChild(this.dom);
9234             }
9235             delete El.cache[this.dom.id];
9236         },
9237
9238         /**
9239          * Sets up event handlers to add and remove a css class when the mouse is over this element
9240          * @param {String} className
9241          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9242          * mouseout events for children elements
9243          * @return {Roo.Element} this
9244          */
9245         addClassOnOver : function(className, preventFlicker){
9246             this.on("mouseover", function(){
9247                 Roo.fly(this, '_internal').addClass(className);
9248             }, this.dom);
9249             var removeFn = function(e){
9250                 if(preventFlicker !== true || !e.within(this, true)){
9251                     Roo.fly(this, '_internal').removeClass(className);
9252                 }
9253             };
9254             this.on("mouseout", removeFn, this.dom);
9255             return this;
9256         },
9257
9258         /**
9259          * Sets up event handlers to add and remove a css class when this element has the focus
9260          * @param {String} className
9261          * @return {Roo.Element} this
9262          */
9263         addClassOnFocus : function(className){
9264             this.on("focus", function(){
9265                 Roo.fly(this, '_internal').addClass(className);
9266             }, this.dom);
9267             this.on("blur", function(){
9268                 Roo.fly(this, '_internal').removeClass(className);
9269             }, this.dom);
9270             return this;
9271         },
9272         /**
9273          * 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)
9274          * @param {String} className
9275          * @return {Roo.Element} this
9276          */
9277         addClassOnClick : function(className){
9278             var dom = this.dom;
9279             this.on("mousedown", function(){
9280                 Roo.fly(dom, '_internal').addClass(className);
9281                 var d = Roo.get(document);
9282                 var fn = function(){
9283                     Roo.fly(dom, '_internal').removeClass(className);
9284                     d.removeListener("mouseup", fn);
9285                 };
9286                 d.on("mouseup", fn);
9287             });
9288             return this;
9289         },
9290
9291         /**
9292          * Stops the specified event from bubbling and optionally prevents the default action
9293          * @param {String} eventName
9294          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9295          * @return {Roo.Element} this
9296          */
9297         swallowEvent : function(eventName, preventDefault){
9298             var fn = function(e){
9299                 e.stopPropagation();
9300                 if(preventDefault){
9301                     e.preventDefault();
9302                 }
9303             };
9304             if(eventName instanceof Array){
9305                 for(var i = 0, len = eventName.length; i < len; i++){
9306                      this.on(eventName[i], fn);
9307                 }
9308                 return this;
9309             }
9310             this.on(eventName, fn);
9311             return this;
9312         },
9313
9314         /**
9315          * @private
9316          */
9317       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9318
9319         /**
9320          * Sizes this element to its parent element's dimensions performing
9321          * neccessary box adjustments.
9322          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9323          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9324          * @return {Roo.Element} this
9325          */
9326         fitToParent : function(monitorResize, targetParent) {
9327           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9328           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9329           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9330             return;
9331           }
9332           var p = Roo.get(targetParent || this.dom.parentNode);
9333           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9334           if (monitorResize === true) {
9335             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9336             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9337           }
9338           return this;
9339         },
9340
9341         /**
9342          * Gets the next sibling, skipping text nodes
9343          * @return {HTMLElement} The next sibling or null
9344          */
9345         getNextSibling : function(){
9346             var n = this.dom.nextSibling;
9347             while(n && n.nodeType != 1){
9348                 n = n.nextSibling;
9349             }
9350             return n;
9351         },
9352
9353         /**
9354          * Gets the previous sibling, skipping text nodes
9355          * @return {HTMLElement} The previous sibling or null
9356          */
9357         getPrevSibling : function(){
9358             var n = this.dom.previousSibling;
9359             while(n && n.nodeType != 1){
9360                 n = n.previousSibling;
9361             }
9362             return n;
9363         },
9364
9365
9366         /**
9367          * Appends the passed element(s) to this element
9368          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9369          * @return {Roo.Element} this
9370          */
9371         appendChild: function(el){
9372             el = Roo.get(el);
9373             el.appendTo(this);
9374             return this;
9375         },
9376
9377         /**
9378          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9379          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9380          * automatically generated with the specified attributes.
9381          * @param {HTMLElement} insertBefore (optional) a child element of this element
9382          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9383          * @return {Roo.Element} The new child element
9384          */
9385         createChild: function(config, insertBefore, returnDom){
9386             config = config || {tag:'div'};
9387             if(insertBefore){
9388                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9389             }
9390             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9391         },
9392
9393         /**
9394          * Appends this element to the passed element
9395          * @param {String/HTMLElement/Element} el The new parent element
9396          * @return {Roo.Element} this
9397          */
9398         appendTo: function(el){
9399             el = Roo.getDom(el);
9400             el.appendChild(this.dom);
9401             return this;
9402         },
9403
9404         /**
9405          * Inserts this element before the passed element in the DOM
9406          * @param {String/HTMLElement/Element} el The element to insert before
9407          * @return {Roo.Element} this
9408          */
9409         insertBefore: function(el){
9410             el = Roo.getDom(el);
9411             el.parentNode.insertBefore(this.dom, el);
9412             return this;
9413         },
9414
9415         /**
9416          * Inserts this element after the passed element in the DOM
9417          * @param {String/HTMLElement/Element} el The element to insert after
9418          * @return {Roo.Element} this
9419          */
9420         insertAfter: function(el){
9421             el = Roo.getDom(el);
9422             el.parentNode.insertBefore(this.dom, el.nextSibling);
9423             return this;
9424         },
9425
9426         /**
9427          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9428          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9429          * @return {Roo.Element} The new child
9430          */
9431         insertFirst: function(el, returnDom){
9432             el = el || {};
9433             if(typeof el == 'object' && !el.nodeType){ // dh config
9434                 return this.createChild(el, this.dom.firstChild, returnDom);
9435             }else{
9436                 el = Roo.getDom(el);
9437                 this.dom.insertBefore(el, this.dom.firstChild);
9438                 return !returnDom ? Roo.get(el) : el;
9439             }
9440         },
9441
9442         /**
9443          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9444          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9445          * @param {String} where (optional) 'before' or 'after' defaults to before
9446          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9447          * @return {Roo.Element} the inserted Element
9448          */
9449         insertSibling: function(el, where, returnDom){
9450             where = where ? where.toLowerCase() : 'before';
9451             el = el || {};
9452             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9453
9454             if(typeof el == 'object' && !el.nodeType){ // dh config
9455                 if(where == 'after' && !this.dom.nextSibling){
9456                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9457                 }else{
9458                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9459                 }
9460
9461             }else{
9462                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9463                             where == 'before' ? this.dom : this.dom.nextSibling);
9464                 if(!returnDom){
9465                     rt = Roo.get(rt);
9466                 }
9467             }
9468             return rt;
9469         },
9470
9471         /**
9472          * Creates and wraps this element with another element
9473          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9474          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9475          * @return {HTMLElement/Element} The newly created wrapper element
9476          */
9477         wrap: function(config, returnDom){
9478             if(!config){
9479                 config = {tag: "div"};
9480             }
9481             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9482             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9483             return newEl;
9484         },
9485
9486         /**
9487          * Replaces the passed element with this element
9488          * @param {String/HTMLElement/Element} el The element to replace
9489          * @return {Roo.Element} this
9490          */
9491         replace: function(el){
9492             el = Roo.get(el);
9493             this.insertBefore(el);
9494             el.remove();
9495             return this;
9496         },
9497
9498         /**
9499          * Inserts an html fragment into this element
9500          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9501          * @param {String} html The HTML fragment
9502          * @param {Boolean} returnEl True to return an Roo.Element
9503          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9504          */
9505         insertHtml : function(where, html, returnEl){
9506             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9507             return returnEl ? Roo.get(el) : el;
9508         },
9509
9510         /**
9511          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9512          * @param {Object} o The object with the attributes
9513          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9514          * @return {Roo.Element} this
9515          */
9516         set : function(o, useSet){
9517             var el = this.dom;
9518             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9519             for(var attr in o){
9520                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9521                 if(attr=="cls"){
9522                     el.className = o["cls"];
9523                 }else{
9524                     if(useSet) {
9525                         el.setAttribute(attr, o[attr]);
9526                     } else {
9527                         el[attr] = o[attr];
9528                     }
9529                 }
9530             }
9531             if(o.style){
9532                 Roo.DomHelper.applyStyles(el, o.style);
9533             }
9534             return this;
9535         },
9536
9537         /**
9538          * Convenience method for constructing a KeyMap
9539          * @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:
9540          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9541          * @param {Function} fn The function to call
9542          * @param {Object} scope (optional) The scope of the function
9543          * @return {Roo.KeyMap} The KeyMap created
9544          */
9545         addKeyListener : function(key, fn, scope){
9546             var config;
9547             if(typeof key != "object" || key instanceof Array){
9548                 config = {
9549                     key: key,
9550                     fn: fn,
9551                     scope: scope
9552                 };
9553             }else{
9554                 config = {
9555                     key : key.key,
9556                     shift : key.shift,
9557                     ctrl : key.ctrl,
9558                     alt : key.alt,
9559                     fn: fn,
9560                     scope: scope
9561                 };
9562             }
9563             return new Roo.KeyMap(this, config);
9564         },
9565
9566         /**
9567          * Creates a KeyMap for this element
9568          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9569          * @return {Roo.KeyMap} The KeyMap created
9570          */
9571         addKeyMap : function(config){
9572             return new Roo.KeyMap(this, config);
9573         },
9574
9575         /**
9576          * Returns true if this element is scrollable.
9577          * @return {Boolean}
9578          */
9579          isScrollable : function(){
9580             var dom = this.dom;
9581             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9582         },
9583
9584         /**
9585          * 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().
9586          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9587          * @param {Number} value The new scroll value
9588          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9589          * @return {Element} this
9590          */
9591
9592         scrollTo : function(side, value, animate){
9593             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9594             if(!animate || !A){
9595                 this.dom[prop] = value;
9596             }else{
9597                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9598                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9599             }
9600             return this;
9601         },
9602
9603         /**
9604          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9605          * within this element's scrollable range.
9606          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9607          * @param {Number} distance How far to scroll the element in pixels
9608          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9609          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9610          * was scrolled as far as it could go.
9611          */
9612          scroll : function(direction, distance, animate){
9613              if(!this.isScrollable()){
9614                  return;
9615              }
9616              var el = this.dom;
9617              var l = el.scrollLeft, t = el.scrollTop;
9618              var w = el.scrollWidth, h = el.scrollHeight;
9619              var cw = el.clientWidth, ch = el.clientHeight;
9620              direction = direction.toLowerCase();
9621              var scrolled = false;
9622              var a = this.preanim(arguments, 2);
9623              switch(direction){
9624                  case "l":
9625                  case "left":
9626                      if(w - l > cw){
9627                          var v = Math.min(l + distance, w-cw);
9628                          this.scrollTo("left", v, a);
9629                          scrolled = true;
9630                      }
9631                      break;
9632                 case "r":
9633                 case "right":
9634                      if(l > 0){
9635                          var v = Math.max(l - distance, 0);
9636                          this.scrollTo("left", v, a);
9637                          scrolled = true;
9638                      }
9639                      break;
9640                 case "t":
9641                 case "top":
9642                 case "up":
9643                      if(t > 0){
9644                          var v = Math.max(t - distance, 0);
9645                          this.scrollTo("top", v, a);
9646                          scrolled = true;
9647                      }
9648                      break;
9649                 case "b":
9650                 case "bottom":
9651                 case "down":
9652                      if(h - t > ch){
9653                          var v = Math.min(t + distance, h-ch);
9654                          this.scrollTo("top", v, a);
9655                          scrolled = true;
9656                      }
9657                      break;
9658              }
9659              return scrolled;
9660         },
9661
9662         /**
9663          * Translates the passed page coordinates into left/top css values for this element
9664          * @param {Number/Array} x The page x or an array containing [x, y]
9665          * @param {Number} y The page y
9666          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9667          */
9668         translatePoints : function(x, y){
9669             if(typeof x == 'object' || x instanceof Array){
9670                 y = x[1]; x = x[0];
9671             }
9672             var p = this.getStyle('position');
9673             var o = this.getXY();
9674
9675             var l = parseInt(this.getStyle('left'), 10);
9676             var t = parseInt(this.getStyle('top'), 10);
9677
9678             if(isNaN(l)){
9679                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9680             }
9681             if(isNaN(t)){
9682                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9683             }
9684
9685             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9686         },
9687
9688         /**
9689          * Returns the current scroll position of the element.
9690          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9691          */
9692         getScroll : function(){
9693             var d = this.dom, doc = document;
9694             if(d == doc || d == doc.body){
9695                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9696                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9697                 return {left: l, top: t};
9698             }else{
9699                 return {left: d.scrollLeft, top: d.scrollTop};
9700             }
9701         },
9702
9703         /**
9704          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9705          * are convert to standard 6 digit hex color.
9706          * @param {String} attr The css attribute
9707          * @param {String} defaultValue The default value to use when a valid color isn't found
9708          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9709          * YUI color anims.
9710          */
9711         getColor : function(attr, defaultValue, prefix){
9712             var v = this.getStyle(attr);
9713             if(!v || v == "transparent" || v == "inherit") {
9714                 return defaultValue;
9715             }
9716             var color = typeof prefix == "undefined" ? "#" : prefix;
9717             if(v.substr(0, 4) == "rgb("){
9718                 var rvs = v.slice(4, v.length -1).split(",");
9719                 for(var i = 0; i < 3; i++){
9720                     var h = parseInt(rvs[i]).toString(16);
9721                     if(h < 16){
9722                         h = "0" + h;
9723                     }
9724                     color += h;
9725                 }
9726             } else {
9727                 if(v.substr(0, 1) == "#"){
9728                     if(v.length == 4) {
9729                         for(var i = 1; i < 4; i++){
9730                             var c = v.charAt(i);
9731                             color +=  c + c;
9732                         }
9733                     }else if(v.length == 7){
9734                         color += v.substr(1);
9735                     }
9736                 }
9737             }
9738             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9739         },
9740
9741         /**
9742          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9743          * gradient background, rounded corners and a 4-way shadow.
9744          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9745          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9746          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9747          * @return {Roo.Element} this
9748          */
9749         boxWrap : function(cls){
9750             cls = cls || 'x-box';
9751             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9752             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9753             return el;
9754         },
9755
9756         /**
9757          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9758          * @param {String} namespace The namespace in which to look for the attribute
9759          * @param {String} name The attribute name
9760          * @return {String} The attribute value
9761          */
9762         getAttributeNS : Roo.isIE ? function(ns, name){
9763             var d = this.dom;
9764             var type = typeof d[ns+":"+name];
9765             if(type != 'undefined' && type != 'unknown'){
9766                 return d[ns+":"+name];
9767             }
9768             return d[name];
9769         } : function(ns, name){
9770             var d = this.dom;
9771             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9772         },
9773         
9774         
9775         /**
9776          * Sets or Returns the value the dom attribute value
9777          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9778          * @param {String} value (optional) The value to set the attribute to
9779          * @return {String} The attribute value
9780          */
9781         attr : function(name){
9782             if (arguments.length > 1) {
9783                 this.dom.setAttribute(name, arguments[1]);
9784                 return arguments[1];
9785             }
9786             if (typeof(name) == 'object') {
9787                 for(var i in name) {
9788                     this.attr(i, name[i]);
9789                 }
9790                 return name;
9791             }
9792             
9793             
9794             if (!this.dom.hasAttribute(name)) {
9795                 return undefined;
9796             }
9797             return this.dom.getAttribute(name);
9798         }
9799         
9800         
9801         
9802     };
9803
9804     var ep = El.prototype;
9805
9806     /**
9807      * Appends an event handler (Shorthand for addListener)
9808      * @param {String}   eventName     The type of event to append
9809      * @param {Function} fn        The method the event invokes
9810      * @param {Object} scope       (optional) The scope (this object) of the fn
9811      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9812      * @method
9813      */
9814     ep.on = ep.addListener;
9815         // backwards compat
9816     ep.mon = ep.addListener;
9817
9818     /**
9819      * Removes an event handler from this element (shorthand for removeListener)
9820      * @param {String} eventName the type of event to remove
9821      * @param {Function} fn the method the event invokes
9822      * @return {Roo.Element} this
9823      * @method
9824      */
9825     ep.un = ep.removeListener;
9826
9827     /**
9828      * true to automatically adjust width and height settings for box-model issues (default to true)
9829      */
9830     ep.autoBoxAdjust = true;
9831
9832     // private
9833     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9834
9835     // private
9836     El.addUnits = function(v, defaultUnit){
9837         if(v === "" || v == "auto"){
9838             return v;
9839         }
9840         if(v === undefined){
9841             return '';
9842         }
9843         if(typeof v == "number" || !El.unitPattern.test(v)){
9844             return v + (defaultUnit || 'px');
9845         }
9846         return v;
9847     };
9848
9849     // special markup used throughout Roo when box wrapping elements
9850     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>';
9851     /**
9852      * Visibility mode constant - Use visibility to hide element
9853      * @static
9854      * @type Number
9855      */
9856     El.VISIBILITY = 1;
9857     /**
9858      * Visibility mode constant - Use display to hide element
9859      * @static
9860      * @type Number
9861      */
9862     El.DISPLAY = 2;
9863
9864     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9865     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9866     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9867
9868
9869
9870     /**
9871      * @private
9872      */
9873     El.cache = {};
9874
9875     var docEl;
9876
9877     /**
9878      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9879      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9880      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9881      * @return {Element} The Element object
9882      * @static
9883      */
9884     El.get = function(el){
9885         var ex, elm, id;
9886         if(!el){ return null; }
9887         if(typeof el == "string"){ // element id
9888             if(!(elm = document.getElementById(el))){
9889                 return null;
9890             }
9891             if(ex = El.cache[el]){
9892                 ex.dom = elm;
9893             }else{
9894                 ex = El.cache[el] = new El(elm);
9895             }
9896             return ex;
9897         }else if(el.tagName){ // dom element
9898             if(!(id = el.id)){
9899                 id = Roo.id(el);
9900             }
9901             if(ex = El.cache[id]){
9902                 ex.dom = el;
9903             }else{
9904                 ex = El.cache[id] = new El(el);
9905             }
9906             return ex;
9907         }else if(el instanceof El){
9908             if(el != docEl){
9909                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9910                                                               // catch case where it hasn't been appended
9911                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9912             }
9913             return el;
9914         }else if(el.isComposite){
9915             return el;
9916         }else if(el instanceof Array){
9917             return El.select(el);
9918         }else if(el == document){
9919             // create a bogus element object representing the document object
9920             if(!docEl){
9921                 var f = function(){};
9922                 f.prototype = El.prototype;
9923                 docEl = new f();
9924                 docEl.dom = document;
9925             }
9926             return docEl;
9927         }
9928         return null;
9929     };
9930
9931     // private
9932     El.uncache = function(el){
9933         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9934             if(a[i]){
9935                 delete El.cache[a[i].id || a[i]];
9936             }
9937         }
9938     };
9939
9940     // private
9941     // Garbage collection - uncache elements/purge listeners on orphaned elements
9942     // so we don't hold a reference and cause the browser to retain them
9943     El.garbageCollect = function(){
9944         if(!Roo.enableGarbageCollector){
9945             clearInterval(El.collectorThread);
9946             return;
9947         }
9948         for(var eid in El.cache){
9949             var el = El.cache[eid], d = el.dom;
9950             // -------------------------------------------------------
9951             // Determining what is garbage:
9952             // -------------------------------------------------------
9953             // !d
9954             // dom node is null, definitely garbage
9955             // -------------------------------------------------------
9956             // !d.parentNode
9957             // no parentNode == direct orphan, definitely garbage
9958             // -------------------------------------------------------
9959             // !d.offsetParent && !document.getElementById(eid)
9960             // display none elements have no offsetParent so we will
9961             // also try to look it up by it's id. However, check
9962             // offsetParent first so we don't do unneeded lookups.
9963             // This enables collection of elements that are not orphans
9964             // directly, but somewhere up the line they have an orphan
9965             // parent.
9966             // -------------------------------------------------------
9967             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9968                 delete El.cache[eid];
9969                 if(d && Roo.enableListenerCollection){
9970                     E.purgeElement(d);
9971                 }
9972             }
9973         }
9974     }
9975     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9976
9977
9978     // dom is optional
9979     El.Flyweight = function(dom){
9980         this.dom = dom;
9981     };
9982     El.Flyweight.prototype = El.prototype;
9983
9984     El._flyweights = {};
9985     /**
9986      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9987      * the dom node can be overwritten by other code.
9988      * @param {String/HTMLElement} el The dom node or id
9989      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9990      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9991      * @static
9992      * @return {Element} The shared Element object
9993      */
9994     El.fly = function(el, named){
9995         named = named || '_global';
9996         el = Roo.getDom(el);
9997         if(!el){
9998             return null;
9999         }
10000         if(!El._flyweights[named]){
10001             El._flyweights[named] = new El.Flyweight();
10002         }
10003         El._flyweights[named].dom = el;
10004         return El._flyweights[named];
10005     };
10006
10007     /**
10008      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10009      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10010      * Shorthand of {@link Roo.Element#get}
10011      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10012      * @return {Element} The Element object
10013      * @member Roo
10014      * @method get
10015      */
10016     Roo.get = El.get;
10017     /**
10018      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10019      * the dom node can be overwritten by other code.
10020      * Shorthand of {@link Roo.Element#fly}
10021      * @param {String/HTMLElement} el The dom node or id
10022      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10023      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10024      * @static
10025      * @return {Element} The shared Element object
10026      * @member Roo
10027      * @method fly
10028      */
10029     Roo.fly = El.fly;
10030
10031     // speedy lookup for elements never to box adjust
10032     var noBoxAdjust = Roo.isStrict ? {
10033         select:1
10034     } : {
10035         input:1, select:1, textarea:1
10036     };
10037     if(Roo.isIE || Roo.isGecko){
10038         noBoxAdjust['button'] = 1;
10039     }
10040
10041
10042     Roo.EventManager.on(window, 'unload', function(){
10043         delete El.cache;
10044         delete El._flyweights;
10045     });
10046 })();
10047
10048
10049
10050
10051 if(Roo.DomQuery){
10052     Roo.Element.selectorFunction = Roo.DomQuery.select;
10053 }
10054
10055 Roo.Element.select = function(selector, unique, root){
10056     var els;
10057     if(typeof selector == "string"){
10058         els = Roo.Element.selectorFunction(selector, root);
10059     }else if(selector.length !== undefined){
10060         els = selector;
10061     }else{
10062         throw "Invalid selector";
10063     }
10064     if(unique === true){
10065         return new Roo.CompositeElement(els);
10066     }else{
10067         return new Roo.CompositeElementLite(els);
10068     }
10069 };
10070 /**
10071  * Selects elements based on the passed CSS selector to enable working on them as 1.
10072  * @param {String/Array} selector The CSS selector or an array of elements
10073  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10074  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10075  * @return {CompositeElementLite/CompositeElement}
10076  * @member Roo
10077  * @method select
10078  */
10079 Roo.select = Roo.Element.select;
10080
10081
10082
10083
10084
10085
10086
10087
10088
10089
10090
10091
10092
10093
10094 /*
10095  * Based on:
10096  * Ext JS Library 1.1.1
10097  * Copyright(c) 2006-2007, Ext JS, LLC.
10098  *
10099  * Originally Released Under LGPL - original licence link has changed is not relivant.
10100  *
10101  * Fork - LGPL
10102  * <script type="text/javascript">
10103  */
10104
10105
10106
10107 //Notifies Element that fx methods are available
10108 Roo.enableFx = true;
10109
10110 /**
10111  * @class Roo.Fx
10112  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10113  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10114  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10115  * Element effects to work.</p><br/>
10116  *
10117  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10118  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10119  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10120  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10121  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10122  * expected results and should be done with care.</p><br/>
10123  *
10124  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10125  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10126 <pre>
10127 Value  Description
10128 -----  -----------------------------
10129 tl     The top left corner
10130 t      The center of the top edge
10131 tr     The top right corner
10132 l      The center of the left edge
10133 r      The center of the right edge
10134 bl     The bottom left corner
10135 b      The center of the bottom edge
10136 br     The bottom right corner
10137 </pre>
10138  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10139  * below are common options that can be passed to any Fx method.</b>
10140  * @cfg {Function} callback A function called when the effect is finished
10141  * @cfg {Object} scope The scope of the effect function
10142  * @cfg {String} easing A valid Easing value for the effect
10143  * @cfg {String} afterCls A css class to apply after the effect
10144  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10145  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10146  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10147  * effects that end with the element being visually hidden, ignored otherwise)
10148  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10149  * a function which returns such a specification that will be applied to the Element after the effect finishes
10150  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10151  * @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
10152  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10153  */
10154 Roo.Fx = {
10155         /**
10156          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10157          * origin for the slide effect.  This function automatically handles wrapping the element with
10158          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10159          * Usage:
10160          *<pre><code>
10161 // default: slide the element in from the top
10162 el.slideIn();
10163
10164 // custom: slide the element in from the right with a 2-second duration
10165 el.slideIn('r', { duration: 2 });
10166
10167 // common config options shown with default values
10168 el.slideIn('t', {
10169     easing: 'easeOut',
10170     duration: .5
10171 });
10172 </code></pre>
10173          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10174          * @param {Object} options (optional) Object literal with any of the Fx config options
10175          * @return {Roo.Element} The Element
10176          */
10177     slideIn : function(anchor, o){
10178         var el = this.getFxEl();
10179         o = o || {};
10180
10181         el.queueFx(o, function(){
10182
10183             anchor = anchor || "t";
10184
10185             // fix display to visibility
10186             this.fixDisplay();
10187
10188             // restore values after effect
10189             var r = this.getFxRestore();
10190             var b = this.getBox();
10191             // fixed size for slide
10192             this.setSize(b);
10193
10194             // wrap if needed
10195             var wrap = this.fxWrap(r.pos, o, "hidden");
10196
10197             var st = this.dom.style;
10198             st.visibility = "visible";
10199             st.position = "absolute";
10200
10201             // clear out temp styles after slide and unwrap
10202             var after = function(){
10203                 el.fxUnwrap(wrap, r.pos, o);
10204                 st.width = r.width;
10205                 st.height = r.height;
10206                 el.afterFx(o);
10207             };
10208             // time to calc the positions
10209             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10210
10211             switch(anchor.toLowerCase()){
10212                 case "t":
10213                     wrap.setSize(b.width, 0);
10214                     st.left = st.bottom = "0";
10215                     a = {height: bh};
10216                 break;
10217                 case "l":
10218                     wrap.setSize(0, b.height);
10219                     st.right = st.top = "0";
10220                     a = {width: bw};
10221                 break;
10222                 case "r":
10223                     wrap.setSize(0, b.height);
10224                     wrap.setX(b.right);
10225                     st.left = st.top = "0";
10226                     a = {width: bw, points: pt};
10227                 break;
10228                 case "b":
10229                     wrap.setSize(b.width, 0);
10230                     wrap.setY(b.bottom);
10231                     st.left = st.top = "0";
10232                     a = {height: bh, points: pt};
10233                 break;
10234                 case "tl":
10235                     wrap.setSize(0, 0);
10236                     st.right = st.bottom = "0";
10237                     a = {width: bw, height: bh};
10238                 break;
10239                 case "bl":
10240                     wrap.setSize(0, 0);
10241                     wrap.setY(b.y+b.height);
10242                     st.right = st.top = "0";
10243                     a = {width: bw, height: bh, points: pt};
10244                 break;
10245                 case "br":
10246                     wrap.setSize(0, 0);
10247                     wrap.setXY([b.right, b.bottom]);
10248                     st.left = st.top = "0";
10249                     a = {width: bw, height: bh, points: pt};
10250                 break;
10251                 case "tr":
10252                     wrap.setSize(0, 0);
10253                     wrap.setX(b.x+b.width);
10254                     st.left = st.bottom = "0";
10255                     a = {width: bw, height: bh, points: pt};
10256                 break;
10257             }
10258             this.dom.style.visibility = "visible";
10259             wrap.show();
10260
10261             arguments.callee.anim = wrap.fxanim(a,
10262                 o,
10263                 'motion',
10264                 .5,
10265                 'easeOut', after);
10266         });
10267         return this;
10268     },
10269     
10270         /**
10271          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10272          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10273          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10274          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10275          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10276          * Usage:
10277          *<pre><code>
10278 // default: slide the element out to the top
10279 el.slideOut();
10280
10281 // custom: slide the element out to the right with a 2-second duration
10282 el.slideOut('r', { duration: 2 });
10283
10284 // common config options shown with default values
10285 el.slideOut('t', {
10286     easing: 'easeOut',
10287     duration: .5,
10288     remove: false,
10289     useDisplay: false
10290 });
10291 </code></pre>
10292          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10293          * @param {Object} options (optional) Object literal with any of the Fx config options
10294          * @return {Roo.Element} The Element
10295          */
10296     slideOut : function(anchor, o){
10297         var el = this.getFxEl();
10298         o = o || {};
10299
10300         el.queueFx(o, function(){
10301
10302             anchor = anchor || "t";
10303
10304             // restore values after effect
10305             var r = this.getFxRestore();
10306             
10307             var b = this.getBox();
10308             // fixed size for slide
10309             this.setSize(b);
10310
10311             // wrap if needed
10312             var wrap = this.fxWrap(r.pos, o, "visible");
10313
10314             var st = this.dom.style;
10315             st.visibility = "visible";
10316             st.position = "absolute";
10317
10318             wrap.setSize(b);
10319
10320             var after = function(){
10321                 if(o.useDisplay){
10322                     el.setDisplayed(false);
10323                 }else{
10324                     el.hide();
10325                 }
10326
10327                 el.fxUnwrap(wrap, r.pos, o);
10328
10329                 st.width = r.width;
10330                 st.height = r.height;
10331
10332                 el.afterFx(o);
10333             };
10334
10335             var a, zero = {to: 0};
10336             switch(anchor.toLowerCase()){
10337                 case "t":
10338                     st.left = st.bottom = "0";
10339                     a = {height: zero};
10340                 break;
10341                 case "l":
10342                     st.right = st.top = "0";
10343                     a = {width: zero};
10344                 break;
10345                 case "r":
10346                     st.left = st.top = "0";
10347                     a = {width: zero, points: {to:[b.right, b.y]}};
10348                 break;
10349                 case "b":
10350                     st.left = st.top = "0";
10351                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10352                 break;
10353                 case "tl":
10354                     st.right = st.bottom = "0";
10355                     a = {width: zero, height: zero};
10356                 break;
10357                 case "bl":
10358                     st.right = st.top = "0";
10359                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10360                 break;
10361                 case "br":
10362                     st.left = st.top = "0";
10363                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10364                 break;
10365                 case "tr":
10366                     st.left = st.bottom = "0";
10367                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10368                 break;
10369             }
10370
10371             arguments.callee.anim = wrap.fxanim(a,
10372                 o,
10373                 'motion',
10374                 .5,
10375                 "easeOut", after);
10376         });
10377         return this;
10378     },
10379
10380         /**
10381          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10382          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10383          * The element must be removed from the DOM using the 'remove' config option if desired.
10384          * Usage:
10385          *<pre><code>
10386 // default
10387 el.puff();
10388
10389 // common config options shown with default values
10390 el.puff({
10391     easing: 'easeOut',
10392     duration: .5,
10393     remove: false,
10394     useDisplay: false
10395 });
10396 </code></pre>
10397          * @param {Object} options (optional) Object literal with any of the Fx config options
10398          * @return {Roo.Element} The Element
10399          */
10400     puff : function(o){
10401         var el = this.getFxEl();
10402         o = o || {};
10403
10404         el.queueFx(o, function(){
10405             this.clearOpacity();
10406             this.show();
10407
10408             // restore values after effect
10409             var r = this.getFxRestore();
10410             var st = this.dom.style;
10411
10412             var after = function(){
10413                 if(o.useDisplay){
10414                     el.setDisplayed(false);
10415                 }else{
10416                     el.hide();
10417                 }
10418
10419                 el.clearOpacity();
10420
10421                 el.setPositioning(r.pos);
10422                 st.width = r.width;
10423                 st.height = r.height;
10424                 st.fontSize = '';
10425                 el.afterFx(o);
10426             };
10427
10428             var width = this.getWidth();
10429             var height = this.getHeight();
10430
10431             arguments.callee.anim = this.fxanim({
10432                     width : {to: this.adjustWidth(width * 2)},
10433                     height : {to: this.adjustHeight(height * 2)},
10434                     points : {by: [-(width * .5), -(height * .5)]},
10435                     opacity : {to: 0},
10436                     fontSize: {to:200, unit: "%"}
10437                 },
10438                 o,
10439                 'motion',
10440                 .5,
10441                 "easeOut", after);
10442         });
10443         return this;
10444     },
10445
10446         /**
10447          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10448          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10449          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10450          * Usage:
10451          *<pre><code>
10452 // default
10453 el.switchOff();
10454
10455 // all config options shown with default values
10456 el.switchOff({
10457     easing: 'easeIn',
10458     duration: .3,
10459     remove: false,
10460     useDisplay: false
10461 });
10462 </code></pre>
10463          * @param {Object} options (optional) Object literal with any of the Fx config options
10464          * @return {Roo.Element} The Element
10465          */
10466     switchOff : function(o){
10467         var el = this.getFxEl();
10468         o = o || {};
10469
10470         el.queueFx(o, function(){
10471             this.clearOpacity();
10472             this.clip();
10473
10474             // restore values after effect
10475             var r = this.getFxRestore();
10476             var st = this.dom.style;
10477
10478             var after = function(){
10479                 if(o.useDisplay){
10480                     el.setDisplayed(false);
10481                 }else{
10482                     el.hide();
10483                 }
10484
10485                 el.clearOpacity();
10486                 el.setPositioning(r.pos);
10487                 st.width = r.width;
10488                 st.height = r.height;
10489
10490                 el.afterFx(o);
10491             };
10492
10493             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10494                 this.clearOpacity();
10495                 (function(){
10496                     this.fxanim({
10497                         height:{to:1},
10498                         points:{by:[0, this.getHeight() * .5]}
10499                     }, o, 'motion', 0.3, 'easeIn', after);
10500                 }).defer(100, this);
10501             });
10502         });
10503         return this;
10504     },
10505
10506     /**
10507      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10508      * changed using the "attr" config option) and then fading back to the original color. If no original
10509      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10510      * Usage:
10511 <pre><code>
10512 // default: highlight background to yellow
10513 el.highlight();
10514
10515 // custom: highlight foreground text to blue for 2 seconds
10516 el.highlight("0000ff", { attr: 'color', duration: 2 });
10517
10518 // common config options shown with default values
10519 el.highlight("ffff9c", {
10520     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10521     endColor: (current color) or "ffffff",
10522     easing: 'easeIn',
10523     duration: 1
10524 });
10525 </code></pre>
10526      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10527      * @param {Object} options (optional) Object literal with any of the Fx config options
10528      * @return {Roo.Element} The Element
10529      */ 
10530     highlight : function(color, o){
10531         var el = this.getFxEl();
10532         o = o || {};
10533
10534         el.queueFx(o, function(){
10535             color = color || "ffff9c";
10536             attr = o.attr || "backgroundColor";
10537
10538             this.clearOpacity();
10539             this.show();
10540
10541             var origColor = this.getColor(attr);
10542             var restoreColor = this.dom.style[attr];
10543             endColor = (o.endColor || origColor) || "ffffff";
10544
10545             var after = function(){
10546                 el.dom.style[attr] = restoreColor;
10547                 el.afterFx(o);
10548             };
10549
10550             var a = {};
10551             a[attr] = {from: color, to: endColor};
10552             arguments.callee.anim = this.fxanim(a,
10553                 o,
10554                 'color',
10555                 1,
10556                 'easeIn', after);
10557         });
10558         return this;
10559     },
10560
10561    /**
10562     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10563     * Usage:
10564 <pre><code>
10565 // default: a single light blue ripple
10566 el.frame();
10567
10568 // custom: 3 red ripples lasting 3 seconds total
10569 el.frame("ff0000", 3, { duration: 3 });
10570
10571 // common config options shown with default values
10572 el.frame("C3DAF9", 1, {
10573     duration: 1 //duration of entire animation (not each individual ripple)
10574     // Note: Easing is not configurable and will be ignored if included
10575 });
10576 </code></pre>
10577     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10578     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10579     * @param {Object} options (optional) Object literal with any of the Fx config options
10580     * @return {Roo.Element} The Element
10581     */
10582     frame : function(color, count, o){
10583         var el = this.getFxEl();
10584         o = o || {};
10585
10586         el.queueFx(o, function(){
10587             color = color || "#C3DAF9";
10588             if(color.length == 6){
10589                 color = "#" + color;
10590             }
10591             count = count || 1;
10592             duration = o.duration || 1;
10593             this.show();
10594
10595             var b = this.getBox();
10596             var animFn = function(){
10597                 var proxy = this.createProxy({
10598
10599                      style:{
10600                         visbility:"hidden",
10601                         position:"absolute",
10602                         "z-index":"35000", // yee haw
10603                         border:"0px solid " + color
10604                      }
10605                   });
10606                 var scale = Roo.isBorderBox ? 2 : 1;
10607                 proxy.animate({
10608                     top:{from:b.y, to:b.y - 20},
10609                     left:{from:b.x, to:b.x - 20},
10610                     borderWidth:{from:0, to:10},
10611                     opacity:{from:1, to:0},
10612                     height:{from:b.height, to:(b.height + (20*scale))},
10613                     width:{from:b.width, to:(b.width + (20*scale))}
10614                 }, duration, function(){
10615                     proxy.remove();
10616                 });
10617                 if(--count > 0){
10618                      animFn.defer((duration/2)*1000, this);
10619                 }else{
10620                     el.afterFx(o);
10621                 }
10622             };
10623             animFn.call(this);
10624         });
10625         return this;
10626     },
10627
10628    /**
10629     * Creates a pause before any subsequent queued effects begin.  If there are
10630     * no effects queued after the pause it will have no effect.
10631     * Usage:
10632 <pre><code>
10633 el.pause(1);
10634 </code></pre>
10635     * @param {Number} seconds The length of time to pause (in seconds)
10636     * @return {Roo.Element} The Element
10637     */
10638     pause : function(seconds){
10639         var el = this.getFxEl();
10640         var o = {};
10641
10642         el.queueFx(o, function(){
10643             setTimeout(function(){
10644                 el.afterFx(o);
10645             }, seconds * 1000);
10646         });
10647         return this;
10648     },
10649
10650    /**
10651     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10652     * using the "endOpacity" config option.
10653     * Usage:
10654 <pre><code>
10655 // default: fade in from opacity 0 to 100%
10656 el.fadeIn();
10657
10658 // custom: fade in from opacity 0 to 75% over 2 seconds
10659 el.fadeIn({ endOpacity: .75, duration: 2});
10660
10661 // common config options shown with default values
10662 el.fadeIn({
10663     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10664     easing: 'easeOut',
10665     duration: .5
10666 });
10667 </code></pre>
10668     * @param {Object} options (optional) Object literal with any of the Fx config options
10669     * @return {Roo.Element} The Element
10670     */
10671     fadeIn : function(o){
10672         var el = this.getFxEl();
10673         o = o || {};
10674         el.queueFx(o, function(){
10675             this.setOpacity(0);
10676             this.fixDisplay();
10677             this.dom.style.visibility = 'visible';
10678             var to = o.endOpacity || 1;
10679             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10680                 o, null, .5, "easeOut", function(){
10681                 if(to == 1){
10682                     this.clearOpacity();
10683                 }
10684                 el.afterFx(o);
10685             });
10686         });
10687         return this;
10688     },
10689
10690    /**
10691     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10692     * using the "endOpacity" config option.
10693     * Usage:
10694 <pre><code>
10695 // default: fade out from the element's current opacity to 0
10696 el.fadeOut();
10697
10698 // custom: fade out from the element's current opacity to 25% over 2 seconds
10699 el.fadeOut({ endOpacity: .25, duration: 2});
10700
10701 // common config options shown with default values
10702 el.fadeOut({
10703     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10704     easing: 'easeOut',
10705     duration: .5
10706     remove: false,
10707     useDisplay: false
10708 });
10709 </code></pre>
10710     * @param {Object} options (optional) Object literal with any of the Fx config options
10711     * @return {Roo.Element} The Element
10712     */
10713     fadeOut : function(o){
10714         var el = this.getFxEl();
10715         o = o || {};
10716         el.queueFx(o, function(){
10717             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10718                 o, null, .5, "easeOut", function(){
10719                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10720                      this.dom.style.display = "none";
10721                 }else{
10722                      this.dom.style.visibility = "hidden";
10723                 }
10724                 this.clearOpacity();
10725                 el.afterFx(o);
10726             });
10727         });
10728         return this;
10729     },
10730
10731    /**
10732     * Animates the transition of an element's dimensions from a starting height/width
10733     * to an ending height/width.
10734     * Usage:
10735 <pre><code>
10736 // change height and width to 100x100 pixels
10737 el.scale(100, 100);
10738
10739 // common config options shown with default values.  The height and width will default to
10740 // the element's existing values if passed as null.
10741 el.scale(
10742     [element's width],
10743     [element's height], {
10744     easing: 'easeOut',
10745     duration: .35
10746 });
10747 </code></pre>
10748     * @param {Number} width  The new width (pass undefined to keep the original width)
10749     * @param {Number} height  The new height (pass undefined to keep the original height)
10750     * @param {Object} options (optional) Object literal with any of the Fx config options
10751     * @return {Roo.Element} The Element
10752     */
10753     scale : function(w, h, o){
10754         this.shift(Roo.apply({}, o, {
10755             width: w,
10756             height: h
10757         }));
10758         return this;
10759     },
10760
10761    /**
10762     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10763     * Any of these properties not specified in the config object will not be changed.  This effect 
10764     * requires that at least one new dimension, position or opacity setting must be passed in on
10765     * the config object in order for the function to have any effect.
10766     * Usage:
10767 <pre><code>
10768 // slide the element horizontally to x position 200 while changing the height and opacity
10769 el.shift({ x: 200, height: 50, opacity: .8 });
10770
10771 // common config options shown with default values.
10772 el.shift({
10773     width: [element's width],
10774     height: [element's height],
10775     x: [element's x position],
10776     y: [element's y position],
10777     opacity: [element's opacity],
10778     easing: 'easeOut',
10779     duration: .35
10780 });
10781 </code></pre>
10782     * @param {Object} options  Object literal with any of the Fx config options
10783     * @return {Roo.Element} The Element
10784     */
10785     shift : function(o){
10786         var el = this.getFxEl();
10787         o = o || {};
10788         el.queueFx(o, function(){
10789             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10790             if(w !== undefined){
10791                 a.width = {to: this.adjustWidth(w)};
10792             }
10793             if(h !== undefined){
10794                 a.height = {to: this.adjustHeight(h)};
10795             }
10796             if(x !== undefined || y !== undefined){
10797                 a.points = {to: [
10798                     x !== undefined ? x : this.getX(),
10799                     y !== undefined ? y : this.getY()
10800                 ]};
10801             }
10802             if(op !== undefined){
10803                 a.opacity = {to: op};
10804             }
10805             if(o.xy !== undefined){
10806                 a.points = {to: o.xy};
10807             }
10808             arguments.callee.anim = this.fxanim(a,
10809                 o, 'motion', .35, "easeOut", function(){
10810                 el.afterFx(o);
10811             });
10812         });
10813         return this;
10814     },
10815
10816         /**
10817          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10818          * ending point of the effect.
10819          * Usage:
10820          *<pre><code>
10821 // default: slide the element downward while fading out
10822 el.ghost();
10823
10824 // custom: slide the element out to the right with a 2-second duration
10825 el.ghost('r', { duration: 2 });
10826
10827 // common config options shown with default values
10828 el.ghost('b', {
10829     easing: 'easeOut',
10830     duration: .5
10831     remove: false,
10832     useDisplay: false
10833 });
10834 </code></pre>
10835          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10836          * @param {Object} options (optional) Object literal with any of the Fx config options
10837          * @return {Roo.Element} The Element
10838          */
10839     ghost : function(anchor, o){
10840         var el = this.getFxEl();
10841         o = o || {};
10842
10843         el.queueFx(o, function(){
10844             anchor = anchor || "b";
10845
10846             // restore values after effect
10847             var r = this.getFxRestore();
10848             var w = this.getWidth(),
10849                 h = this.getHeight();
10850
10851             var st = this.dom.style;
10852
10853             var after = function(){
10854                 if(o.useDisplay){
10855                     el.setDisplayed(false);
10856                 }else{
10857                     el.hide();
10858                 }
10859
10860                 el.clearOpacity();
10861                 el.setPositioning(r.pos);
10862                 st.width = r.width;
10863                 st.height = r.height;
10864
10865                 el.afterFx(o);
10866             };
10867
10868             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10869             switch(anchor.toLowerCase()){
10870                 case "t":
10871                     pt.by = [0, -h];
10872                 break;
10873                 case "l":
10874                     pt.by = [-w, 0];
10875                 break;
10876                 case "r":
10877                     pt.by = [w, 0];
10878                 break;
10879                 case "b":
10880                     pt.by = [0, h];
10881                 break;
10882                 case "tl":
10883                     pt.by = [-w, -h];
10884                 break;
10885                 case "bl":
10886                     pt.by = [-w, h];
10887                 break;
10888                 case "br":
10889                     pt.by = [w, h];
10890                 break;
10891                 case "tr":
10892                     pt.by = [w, -h];
10893                 break;
10894             }
10895
10896             arguments.callee.anim = this.fxanim(a,
10897                 o,
10898                 'motion',
10899                 .5,
10900                 "easeOut", after);
10901         });
10902         return this;
10903     },
10904
10905         /**
10906          * Ensures that all effects queued after syncFx is called on the element are
10907          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10908          * @return {Roo.Element} The Element
10909          */
10910     syncFx : function(){
10911         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10912             block : false,
10913             concurrent : true,
10914             stopFx : false
10915         });
10916         return this;
10917     },
10918
10919         /**
10920          * Ensures that all effects queued after sequenceFx is called on the element are
10921          * run in sequence.  This is the opposite of {@link #syncFx}.
10922          * @return {Roo.Element} The Element
10923          */
10924     sequenceFx : function(){
10925         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10926             block : false,
10927             concurrent : false,
10928             stopFx : false
10929         });
10930         return this;
10931     },
10932
10933         /* @private */
10934     nextFx : function(){
10935         var ef = this.fxQueue[0];
10936         if(ef){
10937             ef.call(this);
10938         }
10939     },
10940
10941         /**
10942          * Returns true if the element has any effects actively running or queued, else returns false.
10943          * @return {Boolean} True if element has active effects, else false
10944          */
10945     hasActiveFx : function(){
10946         return this.fxQueue && this.fxQueue[0];
10947     },
10948
10949         /**
10950          * Stops any running effects and clears the element's internal effects queue if it contains
10951          * any additional effects that haven't started yet.
10952          * @return {Roo.Element} The Element
10953          */
10954     stopFx : function(){
10955         if(this.hasActiveFx()){
10956             var cur = this.fxQueue[0];
10957             if(cur && cur.anim && cur.anim.isAnimated()){
10958                 this.fxQueue = [cur]; // clear out others
10959                 cur.anim.stop(true);
10960             }
10961         }
10962         return this;
10963     },
10964
10965         /* @private */
10966     beforeFx : function(o){
10967         if(this.hasActiveFx() && !o.concurrent){
10968            if(o.stopFx){
10969                this.stopFx();
10970                return true;
10971            }
10972            return false;
10973         }
10974         return true;
10975     },
10976
10977         /**
10978          * Returns true if the element is currently blocking so that no other effect can be queued
10979          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10980          * used to ensure that an effect initiated by a user action runs to completion prior to the
10981          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10982          * @return {Boolean} True if blocking, else false
10983          */
10984     hasFxBlock : function(){
10985         var q = this.fxQueue;
10986         return q && q[0] && q[0].block;
10987     },
10988
10989         /* @private */
10990     queueFx : function(o, fn){
10991         if(!this.fxQueue){
10992             this.fxQueue = [];
10993         }
10994         if(!this.hasFxBlock()){
10995             Roo.applyIf(o, this.fxDefaults);
10996             if(!o.concurrent){
10997                 var run = this.beforeFx(o);
10998                 fn.block = o.block;
10999                 this.fxQueue.push(fn);
11000                 if(run){
11001                     this.nextFx();
11002                 }
11003             }else{
11004                 fn.call(this);
11005             }
11006         }
11007         return this;
11008     },
11009
11010         /* @private */
11011     fxWrap : function(pos, o, vis){
11012         var wrap;
11013         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11014             var wrapXY;
11015             if(o.fixPosition){
11016                 wrapXY = this.getXY();
11017             }
11018             var div = document.createElement("div");
11019             div.style.visibility = vis;
11020             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11021             wrap.setPositioning(pos);
11022             if(wrap.getStyle("position") == "static"){
11023                 wrap.position("relative");
11024             }
11025             this.clearPositioning('auto');
11026             wrap.clip();
11027             wrap.dom.appendChild(this.dom);
11028             if(wrapXY){
11029                 wrap.setXY(wrapXY);
11030             }
11031         }
11032         return wrap;
11033     },
11034
11035         /* @private */
11036     fxUnwrap : function(wrap, pos, o){
11037         this.clearPositioning();
11038         this.setPositioning(pos);
11039         if(!o.wrap){
11040             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11041             wrap.remove();
11042         }
11043     },
11044
11045         /* @private */
11046     getFxRestore : function(){
11047         var st = this.dom.style;
11048         return {pos: this.getPositioning(), width: st.width, height : st.height};
11049     },
11050
11051         /* @private */
11052     afterFx : function(o){
11053         if(o.afterStyle){
11054             this.applyStyles(o.afterStyle);
11055         }
11056         if(o.afterCls){
11057             this.addClass(o.afterCls);
11058         }
11059         if(o.remove === true){
11060             this.remove();
11061         }
11062         Roo.callback(o.callback, o.scope, [this]);
11063         if(!o.concurrent){
11064             this.fxQueue.shift();
11065             this.nextFx();
11066         }
11067     },
11068
11069         /* @private */
11070     getFxEl : function(){ // support for composite element fx
11071         return Roo.get(this.dom);
11072     },
11073
11074         /* @private */
11075     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11076         animType = animType || 'run';
11077         opt = opt || {};
11078         var anim = Roo.lib.Anim[animType](
11079             this.dom, args,
11080             (opt.duration || defaultDur) || .35,
11081             (opt.easing || defaultEase) || 'easeOut',
11082             function(){
11083                 Roo.callback(cb, this);
11084             },
11085             this
11086         );
11087         opt.anim = anim;
11088         return anim;
11089     }
11090 };
11091
11092 // backwords compat
11093 Roo.Fx.resize = Roo.Fx.scale;
11094
11095 //When included, Roo.Fx is automatically applied to Element so that all basic
11096 //effects are available directly via the Element API
11097 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11098  * Based on:
11099  * Ext JS Library 1.1.1
11100  * Copyright(c) 2006-2007, Ext JS, LLC.
11101  *
11102  * Originally Released Under LGPL - original licence link has changed is not relivant.
11103  *
11104  * Fork - LGPL
11105  * <script type="text/javascript">
11106  */
11107
11108
11109 /**
11110  * @class Roo.CompositeElement
11111  * Standard composite class. Creates a Roo.Element for every element in the collection.
11112  * <br><br>
11113  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11114  * actions will be performed on all the elements in this collection.</b>
11115  * <br><br>
11116  * All methods return <i>this</i> and can be chained.
11117  <pre><code>
11118  var els = Roo.select("#some-el div.some-class", true);
11119  // or select directly from an existing element
11120  var el = Roo.get('some-el');
11121  el.select('div.some-class', true);
11122
11123  els.setWidth(100); // all elements become 100 width
11124  els.hide(true); // all elements fade out and hide
11125  // or
11126  els.setWidth(100).hide(true);
11127  </code></pre>
11128  */
11129 Roo.CompositeElement = function(els){
11130     this.elements = [];
11131     this.addElements(els);
11132 };
11133 Roo.CompositeElement.prototype = {
11134     isComposite: true,
11135     addElements : function(els){
11136         if(!els) {
11137             return this;
11138         }
11139         if(typeof els == "string"){
11140             els = Roo.Element.selectorFunction(els);
11141         }
11142         var yels = this.elements;
11143         var index = yels.length-1;
11144         for(var i = 0, len = els.length; i < len; i++) {
11145                 yels[++index] = Roo.get(els[i]);
11146         }
11147         return this;
11148     },
11149
11150     /**
11151     * Clears this composite and adds the elements returned by the passed selector.
11152     * @param {String/Array} els A string CSS selector, an array of elements or an element
11153     * @return {CompositeElement} this
11154     */
11155     fill : function(els){
11156         this.elements = [];
11157         this.add(els);
11158         return this;
11159     },
11160
11161     /**
11162     * Filters this composite to only elements that match the passed selector.
11163     * @param {String} selector A string CSS selector
11164     * @param {Boolean} inverse return inverse filter (not matches)
11165     * @return {CompositeElement} this
11166     */
11167     filter : function(selector, inverse){
11168         var els = [];
11169         inverse = inverse || false;
11170         this.each(function(el){
11171             var match = inverse ? !el.is(selector) : el.is(selector);
11172             if(match){
11173                 els[els.length] = el.dom;
11174             }
11175         });
11176         this.fill(els);
11177         return this;
11178     },
11179
11180     invoke : function(fn, args){
11181         var els = this.elements;
11182         for(var i = 0, len = els.length; i < len; i++) {
11183                 Roo.Element.prototype[fn].apply(els[i], args);
11184         }
11185         return this;
11186     },
11187     /**
11188     * Adds elements to this composite.
11189     * @param {String/Array} els A string CSS selector, an array of elements or an element
11190     * @return {CompositeElement} this
11191     */
11192     add : function(els){
11193         if(typeof els == "string"){
11194             this.addElements(Roo.Element.selectorFunction(els));
11195         }else if(els.length !== undefined){
11196             this.addElements(els);
11197         }else{
11198             this.addElements([els]);
11199         }
11200         return this;
11201     },
11202     /**
11203     * Calls the passed function passing (el, this, index) for each element in this composite.
11204     * @param {Function} fn The function to call
11205     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11206     * @return {CompositeElement} this
11207     */
11208     each : function(fn, scope){
11209         var els = this.elements;
11210         for(var i = 0, len = els.length; i < len; i++){
11211             if(fn.call(scope || els[i], els[i], this, i) === false) {
11212                 break;
11213             }
11214         }
11215         return this;
11216     },
11217
11218     /**
11219      * Returns the Element object at the specified index
11220      * @param {Number} index
11221      * @return {Roo.Element}
11222      */
11223     item : function(index){
11224         return this.elements[index] || null;
11225     },
11226
11227     /**
11228      * Returns the first Element
11229      * @return {Roo.Element}
11230      */
11231     first : function(){
11232         return this.item(0);
11233     },
11234
11235     /**
11236      * Returns the last Element
11237      * @return {Roo.Element}
11238      */
11239     last : function(){
11240         return this.item(this.elements.length-1);
11241     },
11242
11243     /**
11244      * Returns the number of elements in this composite
11245      * @return Number
11246      */
11247     getCount : function(){
11248         return this.elements.length;
11249     },
11250
11251     /**
11252      * Returns true if this composite contains the passed element
11253      * @return Boolean
11254      */
11255     contains : function(el){
11256         return this.indexOf(el) !== -1;
11257     },
11258
11259     /**
11260      * Returns true if this composite contains the passed element
11261      * @return Boolean
11262      */
11263     indexOf : function(el){
11264         return this.elements.indexOf(Roo.get(el));
11265     },
11266
11267
11268     /**
11269     * Removes the specified element(s).
11270     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11271     * or an array of any of those.
11272     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11273     * @return {CompositeElement} this
11274     */
11275     removeElement : function(el, removeDom){
11276         if(el instanceof Array){
11277             for(var i = 0, len = el.length; i < len; i++){
11278                 this.removeElement(el[i]);
11279             }
11280             return this;
11281         }
11282         var index = typeof el == 'number' ? el : this.indexOf(el);
11283         if(index !== -1){
11284             if(removeDom){
11285                 var d = this.elements[index];
11286                 if(d.dom){
11287                     d.remove();
11288                 }else{
11289                     d.parentNode.removeChild(d);
11290                 }
11291             }
11292             this.elements.splice(index, 1);
11293         }
11294         return this;
11295     },
11296
11297     /**
11298     * Replaces the specified element with the passed element.
11299     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11300     * to replace.
11301     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11302     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11303     * @return {CompositeElement} this
11304     */
11305     replaceElement : function(el, replacement, domReplace){
11306         var index = typeof el == 'number' ? el : this.indexOf(el);
11307         if(index !== -1){
11308             if(domReplace){
11309                 this.elements[index].replaceWith(replacement);
11310             }else{
11311                 this.elements.splice(index, 1, Roo.get(replacement))
11312             }
11313         }
11314         return this;
11315     },
11316
11317     /**
11318      * Removes all elements.
11319      */
11320     clear : function(){
11321         this.elements = [];
11322     }
11323 };
11324 (function(){
11325     Roo.CompositeElement.createCall = function(proto, fnName){
11326         if(!proto[fnName]){
11327             proto[fnName] = function(){
11328                 return this.invoke(fnName, arguments);
11329             };
11330         }
11331     };
11332     for(var fnName in Roo.Element.prototype){
11333         if(typeof Roo.Element.prototype[fnName] == "function"){
11334             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11335         }
11336     };
11337 })();
11338 /*
11339  * Based on:
11340  * Ext JS Library 1.1.1
11341  * Copyright(c) 2006-2007, Ext JS, LLC.
11342  *
11343  * Originally Released Under LGPL - original licence link has changed is not relivant.
11344  *
11345  * Fork - LGPL
11346  * <script type="text/javascript">
11347  */
11348
11349 /**
11350  * @class Roo.CompositeElementLite
11351  * @extends Roo.CompositeElement
11352  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11353  <pre><code>
11354  var els = Roo.select("#some-el div.some-class");
11355  // or select directly from an existing element
11356  var el = Roo.get('some-el');
11357  el.select('div.some-class');
11358
11359  els.setWidth(100); // all elements become 100 width
11360  els.hide(true); // all elements fade out and hide
11361  // or
11362  els.setWidth(100).hide(true);
11363  </code></pre><br><br>
11364  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11365  * actions will be performed on all the elements in this collection.</b>
11366  */
11367 Roo.CompositeElementLite = function(els){
11368     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11369     this.el = new Roo.Element.Flyweight();
11370 };
11371 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11372     addElements : function(els){
11373         if(els){
11374             if(els instanceof Array){
11375                 this.elements = this.elements.concat(els);
11376             }else{
11377                 var yels = this.elements;
11378                 var index = yels.length-1;
11379                 for(var i = 0, len = els.length; i < len; i++) {
11380                     yels[++index] = els[i];
11381                 }
11382             }
11383         }
11384         return this;
11385     },
11386     invoke : function(fn, args){
11387         var els = this.elements;
11388         var el = this.el;
11389         for(var i = 0, len = els.length; i < len; i++) {
11390             el.dom = els[i];
11391                 Roo.Element.prototype[fn].apply(el, args);
11392         }
11393         return this;
11394     },
11395     /**
11396      * Returns a flyweight Element of the dom element object at the specified index
11397      * @param {Number} index
11398      * @return {Roo.Element}
11399      */
11400     item : function(index){
11401         if(!this.elements[index]){
11402             return null;
11403         }
11404         this.el.dom = this.elements[index];
11405         return this.el;
11406     },
11407
11408     // fixes scope with flyweight
11409     addListener : function(eventName, handler, scope, opt){
11410         var els = this.elements;
11411         for(var i = 0, len = els.length; i < len; i++) {
11412             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11413         }
11414         return this;
11415     },
11416
11417     /**
11418     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11419     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11420     * a reference to the dom node, use el.dom.</b>
11421     * @param {Function} fn The function to call
11422     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11423     * @return {CompositeElement} this
11424     */
11425     each : function(fn, scope){
11426         var els = this.elements;
11427         var el = this.el;
11428         for(var i = 0, len = els.length; i < len; i++){
11429             el.dom = els[i];
11430                 if(fn.call(scope || el, el, this, i) === false){
11431                 break;
11432             }
11433         }
11434         return this;
11435     },
11436
11437     indexOf : function(el){
11438         return this.elements.indexOf(Roo.getDom(el));
11439     },
11440
11441     replaceElement : function(el, replacement, domReplace){
11442         var index = typeof el == 'number' ? el : this.indexOf(el);
11443         if(index !== -1){
11444             replacement = Roo.getDom(replacement);
11445             if(domReplace){
11446                 var d = this.elements[index];
11447                 d.parentNode.insertBefore(replacement, d);
11448                 d.parentNode.removeChild(d);
11449             }
11450             this.elements.splice(index, 1, replacement);
11451         }
11452         return this;
11453     }
11454 });
11455 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11456
11457 /*
11458  * Based on:
11459  * Ext JS Library 1.1.1
11460  * Copyright(c) 2006-2007, Ext JS, LLC.
11461  *
11462  * Originally Released Under LGPL - original licence link has changed is not relivant.
11463  *
11464  * Fork - LGPL
11465  * <script type="text/javascript">
11466  */
11467
11468  
11469
11470 /**
11471  * @class Roo.data.Connection
11472  * @extends Roo.util.Observable
11473  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11474  * either to a configured URL, or to a URL specified at request time.<br><br>
11475  * <p>
11476  * Requests made by this class are asynchronous, and will return immediately. No data from
11477  * the server will be available to the statement immediately following the {@link #request} call.
11478  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11479  * <p>
11480  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11481  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11482  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11483  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11484  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11485  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11486  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11487  * standard DOM methods.
11488  * @constructor
11489  * @param {Object} config a configuration object.
11490  */
11491 Roo.data.Connection = function(config){
11492     Roo.apply(this, config);
11493     this.addEvents({
11494         /**
11495          * @event beforerequest
11496          * Fires before a network request is made to retrieve a data object.
11497          * @param {Connection} conn This Connection object.
11498          * @param {Object} options The options config object passed to the {@link #request} method.
11499          */
11500         "beforerequest" : true,
11501         /**
11502          * @event requestcomplete
11503          * Fires if the request was successfully completed.
11504          * @param {Connection} conn This Connection object.
11505          * @param {Object} response The XHR object containing the response data.
11506          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11507          * @param {Object} options The options config object passed to the {@link #request} method.
11508          */
11509         "requestcomplete" : true,
11510         /**
11511          * @event requestexception
11512          * Fires if an error HTTP status was returned from the server.
11513          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11514          * @param {Connection} conn This Connection object.
11515          * @param {Object} response The XHR object containing the response data.
11516          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11517          * @param {Object} options The options config object passed to the {@link #request} method.
11518          */
11519         "requestexception" : true
11520     });
11521     Roo.data.Connection.superclass.constructor.call(this);
11522 };
11523
11524 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11525     /**
11526      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11527      */
11528     /**
11529      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11530      * extra parameters to each request made by this object. (defaults to undefined)
11531      */
11532     /**
11533      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11534      *  to each request made by this object. (defaults to undefined)
11535      */
11536     /**
11537      * @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)
11538      */
11539     /**
11540      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11541      */
11542     timeout : 30000,
11543     /**
11544      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11545      * @type Boolean
11546      */
11547     autoAbort:false,
11548
11549     /**
11550      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11551      * @type Boolean
11552      */
11553     disableCaching: true,
11554
11555     /**
11556      * Sends an HTTP request to a remote server.
11557      * @param {Object} options An object which may contain the following properties:<ul>
11558      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11559      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11560      * request, a url encoded string or a function to call to get either.</li>
11561      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11562      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11563      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11564      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11565      * <li>options {Object} The parameter to the request call.</li>
11566      * <li>success {Boolean} True if the request succeeded.</li>
11567      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11568      * </ul></li>
11569      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11570      * The callback is passed the following parameters:<ul>
11571      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11572      * <li>options {Object} The parameter to the request call.</li>
11573      * </ul></li>
11574      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11575      * The callback is passed the following parameters:<ul>
11576      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11577      * <li>options {Object} The parameter to the request call.</li>
11578      * </ul></li>
11579      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11580      * for the callback function. Defaults to the browser window.</li>
11581      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11582      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11583      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11584      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11585      * params for the post data. Any params will be appended to the URL.</li>
11586      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11587      * </ul>
11588      * @return {Number} transactionId
11589      */
11590     request : function(o){
11591         if(this.fireEvent("beforerequest", this, o) !== false){
11592             var p = o.params;
11593
11594             if(typeof p == "function"){
11595                 p = p.call(o.scope||window, o);
11596             }
11597             if(typeof p == "object"){
11598                 p = Roo.urlEncode(o.params);
11599             }
11600             if(this.extraParams){
11601                 var extras = Roo.urlEncode(this.extraParams);
11602                 p = p ? (p + '&' + extras) : extras;
11603             }
11604
11605             var url = o.url || this.url;
11606             if(typeof url == 'function'){
11607                 url = url.call(o.scope||window, o);
11608             }
11609
11610             if(o.form){
11611                 var form = Roo.getDom(o.form);
11612                 url = url || form.action;
11613
11614                 var enctype = form.getAttribute("enctype");
11615                 
11616                 if (o.formData) {
11617                     return this.doFormDataUpload(o,p,url);
11618                 }
11619                 
11620                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11621                     return this.doFormUpload(o, p, url);
11622                 }
11623                 var f = Roo.lib.Ajax.serializeForm(form);
11624                 p = p ? (p + '&' + f) : f;
11625             }
11626
11627             var hs = o.headers;
11628             if(this.defaultHeaders){
11629                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11630                 if(!o.headers){
11631                     o.headers = hs;
11632                 }
11633             }
11634
11635             var cb = {
11636                 success: this.handleResponse,
11637                 failure: this.handleFailure,
11638                 scope: this,
11639                 argument: {options: o},
11640                 timeout : o.timeout || this.timeout
11641             };
11642
11643             var method = o.method||this.method||(p ? "POST" : "GET");
11644
11645             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11646                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11647             }
11648
11649             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11650                 if(o.autoAbort){
11651                     this.abort();
11652                 }
11653             }else if(this.autoAbort !== false){
11654                 this.abort();
11655             }
11656
11657             if((method == 'GET' && p) || o.xmlData){
11658                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11659                 p = '';
11660             }
11661             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11662             return this.transId;
11663         }else{
11664             Roo.callback(o.callback, o.scope, [o, null, null]);
11665             return null;
11666         }
11667     },
11668
11669     /**
11670      * Determine whether this object has a request outstanding.
11671      * @param {Number} transactionId (Optional) defaults to the last transaction
11672      * @return {Boolean} True if there is an outstanding request.
11673      */
11674     isLoading : function(transId){
11675         if(transId){
11676             return Roo.lib.Ajax.isCallInProgress(transId);
11677         }else{
11678             return this.transId ? true : false;
11679         }
11680     },
11681
11682     /**
11683      * Aborts any outstanding request.
11684      * @param {Number} transactionId (Optional) defaults to the last transaction
11685      */
11686     abort : function(transId){
11687         if(transId || this.isLoading()){
11688             Roo.lib.Ajax.abort(transId || this.transId);
11689         }
11690     },
11691
11692     // private
11693     handleResponse : function(response){
11694         this.transId = false;
11695         var options = response.argument.options;
11696         response.argument = options ? options.argument : null;
11697         this.fireEvent("requestcomplete", this, response, options);
11698         Roo.callback(options.success, options.scope, [response, options]);
11699         Roo.callback(options.callback, options.scope, [options, true, response]);
11700     },
11701
11702     // private
11703     handleFailure : function(response, e){
11704         this.transId = false;
11705         var options = response.argument.options;
11706         response.argument = options ? options.argument : null;
11707         this.fireEvent("requestexception", this, response, options, e);
11708         Roo.callback(options.failure, options.scope, [response, options]);
11709         Roo.callback(options.callback, options.scope, [options, false, response]);
11710     },
11711
11712     // private
11713     doFormUpload : function(o, ps, url){
11714         var id = Roo.id();
11715         var frame = document.createElement('iframe');
11716         frame.id = id;
11717         frame.name = id;
11718         frame.className = 'x-hidden';
11719         if(Roo.isIE){
11720             frame.src = Roo.SSL_SECURE_URL;
11721         }
11722         document.body.appendChild(frame);
11723
11724         if(Roo.isIE){
11725            document.frames[id].name = id;
11726         }
11727
11728         var form = Roo.getDom(o.form);
11729         form.target = id;
11730         form.method = 'POST';
11731         form.enctype = form.encoding = 'multipart/form-data';
11732         if(url){
11733             form.action = url;
11734         }
11735
11736         var hiddens, hd;
11737         if(ps){ // add dynamic params
11738             hiddens = [];
11739             ps = Roo.urlDecode(ps, false);
11740             for(var k in ps){
11741                 if(ps.hasOwnProperty(k)){
11742                     hd = document.createElement('input');
11743                     hd.type = 'hidden';
11744                     hd.name = k;
11745                     hd.value = ps[k];
11746                     form.appendChild(hd);
11747                     hiddens.push(hd);
11748                 }
11749             }
11750         }
11751
11752         function cb(){
11753             var r = {  // bogus response object
11754                 responseText : '',
11755                 responseXML : null
11756             };
11757
11758             r.argument = o ? o.argument : null;
11759
11760             try { //
11761                 var doc;
11762                 if(Roo.isIE){
11763                     doc = frame.contentWindow.document;
11764                 }else {
11765                     doc = (frame.contentDocument || window.frames[id].document);
11766                 }
11767                 if(doc && doc.body){
11768                     r.responseText = doc.body.innerHTML;
11769                 }
11770                 if(doc && doc.XMLDocument){
11771                     r.responseXML = doc.XMLDocument;
11772                 }else {
11773                     r.responseXML = doc;
11774                 }
11775             }
11776             catch(e) {
11777                 // ignore
11778             }
11779
11780             Roo.EventManager.removeListener(frame, 'load', cb, this);
11781
11782             this.fireEvent("requestcomplete", this, r, o);
11783             Roo.callback(o.success, o.scope, [r, o]);
11784             Roo.callback(o.callback, o.scope, [o, true, r]);
11785
11786             setTimeout(function(){document.body.removeChild(frame);}, 100);
11787         }
11788
11789         Roo.EventManager.on(frame, 'load', cb, this);
11790         form.submit();
11791
11792         if(hiddens){ // remove dynamic params
11793             for(var i = 0, len = hiddens.length; i < len; i++){
11794                 form.removeChild(hiddens[i]);
11795             }
11796         }
11797     },
11798     // this is a 'formdata version???'
11799     
11800     
11801     doFormDataUpload : function(o, ps, url)
11802     {
11803         var form = Roo.getDom(o.form);
11804         form.enctype = form.encoding = 'multipart/form-data';
11805         var formData = o.formData === true ? new FormData(form) : o.formData;
11806       
11807         var cb = {
11808             success: this.handleResponse,
11809             failure: this.handleFailure,
11810             scope: this,
11811             argument: {options: o},
11812             timeout : o.timeout || this.timeout
11813         };
11814  
11815         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11816             if(o.autoAbort){
11817                 this.abort();
11818             }
11819         }else if(this.autoAbort !== false){
11820             this.abort();
11821         }
11822
11823         Roo.lib.Ajax.defaultPostHeader = undefined;
11824         this.transId = Roo.lib.Ajax.request( "POST", url, cb, o.formData, o);
11825         Roo.lib.Ajax.defaultPostHeader = 'application/x-www-form-urlencoded';
11826  
11827          
11828     }
11829     
11830 });
11831 /*
11832  * Based on:
11833  * Ext JS Library 1.1.1
11834  * Copyright(c) 2006-2007, Ext JS, LLC.
11835  *
11836  * Originally Released Under LGPL - original licence link has changed is not relivant.
11837  *
11838  * Fork - LGPL
11839  * <script type="text/javascript">
11840  */
11841  
11842 /**
11843  * Global Ajax request class.
11844  * 
11845  * @class Roo.Ajax
11846  * @extends Roo.data.Connection
11847  * @static
11848  * 
11849  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11850  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11851  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11852  * @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)
11853  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11854  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11855  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11856  */
11857 Roo.Ajax = new Roo.data.Connection({
11858     // fix up the docs
11859     /**
11860      * @scope Roo.Ajax
11861      * @type {Boolear} 
11862      */
11863     autoAbort : false,
11864
11865     /**
11866      * Serialize the passed form into a url encoded string
11867      * @scope Roo.Ajax
11868      * @param {String/HTMLElement} form
11869      * @return {String}
11870      */
11871     serializeForm : function(form){
11872         return Roo.lib.Ajax.serializeForm(form);
11873     }
11874 });/*
11875  * Based on:
11876  * Ext JS Library 1.1.1
11877  * Copyright(c) 2006-2007, Ext JS, LLC.
11878  *
11879  * Originally Released Under LGPL - original licence link has changed is not relivant.
11880  *
11881  * Fork - LGPL
11882  * <script type="text/javascript">
11883  */
11884
11885  
11886 /**
11887  * @class Roo.UpdateManager
11888  * @extends Roo.util.Observable
11889  * Provides AJAX-style update for Element object.<br><br>
11890  * Usage:<br>
11891  * <pre><code>
11892  * // Get it from a Roo.Element object
11893  * var el = Roo.get("foo");
11894  * var mgr = el.getUpdateManager();
11895  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11896  * ...
11897  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11898  * <br>
11899  * // or directly (returns the same UpdateManager instance)
11900  * var mgr = new Roo.UpdateManager("myElementId");
11901  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11902  * mgr.on("update", myFcnNeedsToKnow);
11903  * <br>
11904    // short handed call directly from the element object
11905    Roo.get("foo").load({
11906         url: "bar.php",
11907         scripts:true,
11908         params: "for=bar",
11909         text: "Loading Foo..."
11910    });
11911  * </code></pre>
11912  * @constructor
11913  * Create new UpdateManager directly.
11914  * @param {String/HTMLElement/Roo.Element} el The element to update
11915  * @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).
11916  */
11917 Roo.UpdateManager = function(el, forceNew){
11918     el = Roo.get(el);
11919     if(!forceNew && el.updateManager){
11920         return el.updateManager;
11921     }
11922     /**
11923      * The Element object
11924      * @type Roo.Element
11925      */
11926     this.el = el;
11927     /**
11928      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11929      * @type String
11930      */
11931     this.defaultUrl = null;
11932
11933     this.addEvents({
11934         /**
11935          * @event beforeupdate
11936          * Fired before an update is made, return false from your handler and the update is cancelled.
11937          * @param {Roo.Element} el
11938          * @param {String/Object/Function} url
11939          * @param {String/Object} params
11940          */
11941         "beforeupdate": true,
11942         /**
11943          * @event update
11944          * Fired after successful update is made.
11945          * @param {Roo.Element} el
11946          * @param {Object} oResponseObject The response Object
11947          */
11948         "update": true,
11949         /**
11950          * @event failure
11951          * Fired on update failure.
11952          * @param {Roo.Element} el
11953          * @param {Object} oResponseObject The response Object
11954          */
11955         "failure": true
11956     });
11957     var d = Roo.UpdateManager.defaults;
11958     /**
11959      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11960      * @type String
11961      */
11962     this.sslBlankUrl = d.sslBlankUrl;
11963     /**
11964      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11965      * @type Boolean
11966      */
11967     this.disableCaching = d.disableCaching;
11968     /**
11969      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11970      * @type String
11971      */
11972     this.indicatorText = d.indicatorText;
11973     /**
11974      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11975      * @type String
11976      */
11977     this.showLoadIndicator = d.showLoadIndicator;
11978     /**
11979      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11980      * @type Number
11981      */
11982     this.timeout = d.timeout;
11983
11984     /**
11985      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11986      * @type Boolean
11987      */
11988     this.loadScripts = d.loadScripts;
11989
11990     /**
11991      * Transaction object of current executing transaction
11992      */
11993     this.transaction = null;
11994
11995     /**
11996      * @private
11997      */
11998     this.autoRefreshProcId = null;
11999     /**
12000      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12001      * @type Function
12002      */
12003     this.refreshDelegate = this.refresh.createDelegate(this);
12004     /**
12005      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12006      * @type Function
12007      */
12008     this.updateDelegate = this.update.createDelegate(this);
12009     /**
12010      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12011      * @type Function
12012      */
12013     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12014     /**
12015      * @private
12016      */
12017     this.successDelegate = this.processSuccess.createDelegate(this);
12018     /**
12019      * @private
12020      */
12021     this.failureDelegate = this.processFailure.createDelegate(this);
12022
12023     if(!this.renderer){
12024      /**
12025       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12026       */
12027     this.renderer = new Roo.UpdateManager.BasicRenderer();
12028     }
12029     
12030     Roo.UpdateManager.superclass.constructor.call(this);
12031 };
12032
12033 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12034     /**
12035      * Get the Element this UpdateManager is bound to
12036      * @return {Roo.Element} The element
12037      */
12038     getEl : function(){
12039         return this.el;
12040     },
12041     /**
12042      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12043      * @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:
12044 <pre><code>
12045 um.update({<br/>
12046     url: "your-url.php",<br/>
12047     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12048     callback: yourFunction,<br/>
12049     scope: yourObject, //(optional scope)  <br/>
12050     discardUrl: false, <br/>
12051     nocache: false,<br/>
12052     text: "Loading...",<br/>
12053     timeout: 30,<br/>
12054     scripts: false<br/>
12055 });
12056 </code></pre>
12057      * The only required property is url. The optional properties nocache, text and scripts
12058      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12059      * @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}
12060      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12061      * @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.
12062      */
12063     update : function(url, params, callback, discardUrl){
12064         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12065             var method = this.method,
12066                 cfg;
12067             if(typeof url == "object"){ // must be config object
12068                 cfg = url;
12069                 url = cfg.url;
12070                 params = params || cfg.params;
12071                 callback = callback || cfg.callback;
12072                 discardUrl = discardUrl || cfg.discardUrl;
12073                 if(callback && cfg.scope){
12074                     callback = callback.createDelegate(cfg.scope);
12075                 }
12076                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12077                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12078                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12079                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12080                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12081             }
12082             this.showLoading();
12083             if(!discardUrl){
12084                 this.defaultUrl = url;
12085             }
12086             if(typeof url == "function"){
12087                 url = url.call(this);
12088             }
12089
12090             method = method || (params ? "POST" : "GET");
12091             if(method == "GET"){
12092                 url = this.prepareUrl(url);
12093             }
12094
12095             var o = Roo.apply(cfg ||{}, {
12096                 url : url,
12097                 params: params,
12098                 success: this.successDelegate,
12099                 failure: this.failureDelegate,
12100                 callback: undefined,
12101                 timeout: (this.timeout*1000),
12102                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12103             });
12104             Roo.log("updated manager called with timeout of " + o.timeout);
12105             this.transaction = Roo.Ajax.request(o);
12106         }
12107     },
12108
12109     /**
12110      * 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.
12111      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12112      * @param {String/HTMLElement} form The form Id or form element
12113      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12114      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12115      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12116      */
12117     formUpdate : function(form, url, reset, callback){
12118         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12119             if(typeof url == "function"){
12120                 url = url.call(this);
12121             }
12122             form = Roo.getDom(form);
12123             this.transaction = Roo.Ajax.request({
12124                 form: form,
12125                 url:url,
12126                 success: this.successDelegate,
12127                 failure: this.failureDelegate,
12128                 timeout: (this.timeout*1000),
12129                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12130             });
12131             this.showLoading.defer(1, this);
12132         }
12133     },
12134
12135     /**
12136      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12137      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12138      */
12139     refresh : function(callback){
12140         if(this.defaultUrl == null){
12141             return;
12142         }
12143         this.update(this.defaultUrl, null, callback, true);
12144     },
12145
12146     /**
12147      * Set this element to auto refresh.
12148      * @param {Number} interval How often to update (in seconds).
12149      * @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)
12150      * @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}
12151      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12152      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12153      */
12154     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12155         if(refreshNow){
12156             this.update(url || this.defaultUrl, params, callback, true);
12157         }
12158         if(this.autoRefreshProcId){
12159             clearInterval(this.autoRefreshProcId);
12160         }
12161         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12162     },
12163
12164     /**
12165      * Stop auto refresh on this element.
12166      */
12167      stopAutoRefresh : function(){
12168         if(this.autoRefreshProcId){
12169             clearInterval(this.autoRefreshProcId);
12170             delete this.autoRefreshProcId;
12171         }
12172     },
12173
12174     isAutoRefreshing : function(){
12175        return this.autoRefreshProcId ? true : false;
12176     },
12177     /**
12178      * Called to update the element to "Loading" state. Override to perform custom action.
12179      */
12180     showLoading : function(){
12181         if(this.showLoadIndicator){
12182             this.el.update(this.indicatorText);
12183         }
12184     },
12185
12186     /**
12187      * Adds unique parameter to query string if disableCaching = true
12188      * @private
12189      */
12190     prepareUrl : function(url){
12191         if(this.disableCaching){
12192             var append = "_dc=" + (new Date().getTime());
12193             if(url.indexOf("?") !== -1){
12194                 url += "&" + append;
12195             }else{
12196                 url += "?" + append;
12197             }
12198         }
12199         return url;
12200     },
12201
12202     /**
12203      * @private
12204      */
12205     processSuccess : function(response){
12206         this.transaction = null;
12207         if(response.argument.form && response.argument.reset){
12208             try{ // put in try/catch since some older FF releases had problems with this
12209                 response.argument.form.reset();
12210             }catch(e){}
12211         }
12212         if(this.loadScripts){
12213             this.renderer.render(this.el, response, this,
12214                 this.updateComplete.createDelegate(this, [response]));
12215         }else{
12216             this.renderer.render(this.el, response, this);
12217             this.updateComplete(response);
12218         }
12219     },
12220
12221     updateComplete : function(response){
12222         this.fireEvent("update", this.el, response);
12223         if(typeof response.argument.callback == "function"){
12224             response.argument.callback(this.el, true, response);
12225         }
12226     },
12227
12228     /**
12229      * @private
12230      */
12231     processFailure : function(response){
12232         this.transaction = null;
12233         this.fireEvent("failure", this.el, response);
12234         if(typeof response.argument.callback == "function"){
12235             response.argument.callback(this.el, false, response);
12236         }
12237     },
12238
12239     /**
12240      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12241      * @param {Object} renderer The object implementing the render() method
12242      */
12243     setRenderer : function(renderer){
12244         this.renderer = renderer;
12245     },
12246
12247     getRenderer : function(){
12248        return this.renderer;
12249     },
12250
12251     /**
12252      * Set the defaultUrl used for updates
12253      * @param {String/Function} defaultUrl The url or a function to call to get the url
12254      */
12255     setDefaultUrl : function(defaultUrl){
12256         this.defaultUrl = defaultUrl;
12257     },
12258
12259     /**
12260      * Aborts the executing transaction
12261      */
12262     abort : function(){
12263         if(this.transaction){
12264             Roo.Ajax.abort(this.transaction);
12265         }
12266     },
12267
12268     /**
12269      * Returns true if an update is in progress
12270      * @return {Boolean}
12271      */
12272     isUpdating : function(){
12273         if(this.transaction){
12274             return Roo.Ajax.isLoading(this.transaction);
12275         }
12276         return false;
12277     }
12278 });
12279
12280 /**
12281  * @class Roo.UpdateManager.defaults
12282  * @static (not really - but it helps the doc tool)
12283  * The defaults collection enables customizing the default properties of UpdateManager
12284  */
12285    Roo.UpdateManager.defaults = {
12286        /**
12287          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12288          * @type Number
12289          */
12290          timeout : 30,
12291
12292          /**
12293          * True to process scripts by default (Defaults to false).
12294          * @type Boolean
12295          */
12296         loadScripts : false,
12297
12298         /**
12299         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12300         * @type String
12301         */
12302         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12303         /**
12304          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12305          * @type Boolean
12306          */
12307         disableCaching : false,
12308         /**
12309          * Whether to show indicatorText when loading (Defaults to true).
12310          * @type Boolean
12311          */
12312         showLoadIndicator : true,
12313         /**
12314          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12315          * @type String
12316          */
12317         indicatorText : '<div class="loading-indicator">Loading...</div>'
12318    };
12319
12320 /**
12321  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12322  *Usage:
12323  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12324  * @param {String/HTMLElement/Roo.Element} el The element to update
12325  * @param {String} url The url
12326  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12327  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12328  * @static
12329  * @deprecated
12330  * @member Roo.UpdateManager
12331  */
12332 Roo.UpdateManager.updateElement = function(el, url, params, options){
12333     var um = Roo.get(el, true).getUpdateManager();
12334     Roo.apply(um, options);
12335     um.update(url, params, options ? options.callback : null);
12336 };
12337 // alias for backwards compat
12338 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12339 /**
12340  * @class Roo.UpdateManager.BasicRenderer
12341  * Default Content renderer. Updates the elements innerHTML with the responseText.
12342  */
12343 Roo.UpdateManager.BasicRenderer = function(){};
12344
12345 Roo.UpdateManager.BasicRenderer.prototype = {
12346     /**
12347      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12348      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12349      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12350      * @param {Roo.Element} el The element being rendered
12351      * @param {Object} response The YUI Connect response object
12352      * @param {UpdateManager} updateManager The calling update manager
12353      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12354      */
12355      render : function(el, response, updateManager, callback){
12356         el.update(response.responseText, updateManager.loadScripts, callback);
12357     }
12358 };
12359 /*
12360  * Based on:
12361  * Roo JS
12362  * (c)) Alan Knowles
12363  * Licence : LGPL
12364  */
12365
12366
12367 /**
12368  * @class Roo.DomTemplate
12369  * @extends Roo.Template
12370  * An effort at a dom based template engine..
12371  *
12372  * Similar to XTemplate, except it uses dom parsing to create the template..
12373  *
12374  * Supported features:
12375  *
12376  *  Tags:
12377
12378 <pre><code>
12379       {a_variable} - output encoded.
12380       {a_variable.format:("Y-m-d")} - call a method on the variable
12381       {a_variable:raw} - unencoded output
12382       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12383       {a_variable:this.method_on_template(...)} - call a method on the template object.
12384  
12385 </code></pre>
12386  *  The tpl tag:
12387 <pre><code>
12388         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12389         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12390         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12391         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12392   
12393 </code></pre>
12394  *      
12395  */
12396 Roo.DomTemplate = function()
12397 {
12398      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12399      if (this.html) {
12400         this.compile();
12401      }
12402 };
12403
12404
12405 Roo.extend(Roo.DomTemplate, Roo.Template, {
12406     /**
12407      * id counter for sub templates.
12408      */
12409     id : 0,
12410     /**
12411      * flag to indicate if dom parser is inside a pre,
12412      * it will strip whitespace if not.
12413      */
12414     inPre : false,
12415     
12416     /**
12417      * The various sub templates
12418      */
12419     tpls : false,
12420     
12421     
12422     
12423     /**
12424      *
12425      * basic tag replacing syntax
12426      * WORD:WORD()
12427      *
12428      * // you can fake an object call by doing this
12429      *  x.t:(test,tesT) 
12430      * 
12431      */
12432     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12433     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12434     
12435     iterChild : function (node, method) {
12436         
12437         var oldPre = this.inPre;
12438         if (node.tagName == 'PRE') {
12439             this.inPre = true;
12440         }
12441         for( var i = 0; i < node.childNodes.length; i++) {
12442             method.call(this, node.childNodes[i]);
12443         }
12444         this.inPre = oldPre;
12445     },
12446     
12447     
12448     
12449     /**
12450      * compile the template
12451      *
12452      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12453      *
12454      */
12455     compile: function()
12456     {
12457         var s = this.html;
12458         
12459         // covert the html into DOM...
12460         var doc = false;
12461         var div =false;
12462         try {
12463             doc = document.implementation.createHTMLDocument("");
12464             doc.documentElement.innerHTML =   this.html  ;
12465             div = doc.documentElement;
12466         } catch (e) {
12467             // old IE... - nasty -- it causes all sorts of issues.. with
12468             // images getting pulled from server..
12469             div = document.createElement('div');
12470             div.innerHTML = this.html;
12471         }
12472         //doc.documentElement.innerHTML = htmlBody
12473          
12474         
12475         
12476         this.tpls = [];
12477         var _t = this;
12478         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12479         
12480         var tpls = this.tpls;
12481         
12482         // create a top level template from the snippet..
12483         
12484         //Roo.log(div.innerHTML);
12485         
12486         var tpl = {
12487             uid : 'master',
12488             id : this.id++,
12489             attr : false,
12490             value : false,
12491             body : div.innerHTML,
12492             
12493             forCall : false,
12494             execCall : false,
12495             dom : div,
12496             isTop : true
12497             
12498         };
12499         tpls.unshift(tpl);
12500         
12501         
12502         // compile them...
12503         this.tpls = [];
12504         Roo.each(tpls, function(tp){
12505             this.compileTpl(tp);
12506             this.tpls[tp.id] = tp;
12507         }, this);
12508         
12509         this.master = tpls[0];
12510         return this;
12511         
12512         
12513     },
12514     
12515     compileNode : function(node, istop) {
12516         // test for
12517         //Roo.log(node);
12518         
12519         
12520         // skip anything not a tag..
12521         if (node.nodeType != 1) {
12522             if (node.nodeType == 3 && !this.inPre) {
12523                 // reduce white space..
12524                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12525                 
12526             }
12527             return;
12528         }
12529         
12530         var tpl = {
12531             uid : false,
12532             id : false,
12533             attr : false,
12534             value : false,
12535             body : '',
12536             
12537             forCall : false,
12538             execCall : false,
12539             dom : false,
12540             isTop : istop
12541             
12542             
12543         };
12544         
12545         
12546         switch(true) {
12547             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12548             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12549             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12550             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12551             // no default..
12552         }
12553         
12554         
12555         if (!tpl.attr) {
12556             // just itterate children..
12557             this.iterChild(node,this.compileNode);
12558             return;
12559         }
12560         tpl.uid = this.id++;
12561         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12562         node.removeAttribute('roo-'+ tpl.attr);
12563         if (tpl.attr != 'name') {
12564             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12565             node.parentNode.replaceChild(placeholder,  node);
12566         } else {
12567             
12568             var placeholder =  document.createElement('span');
12569             placeholder.className = 'roo-tpl-' + tpl.value;
12570             node.parentNode.replaceChild(placeholder,  node);
12571         }
12572         
12573         // parent now sees '{domtplXXXX}
12574         this.iterChild(node,this.compileNode);
12575         
12576         // we should now have node body...
12577         var div = document.createElement('div');
12578         div.appendChild(node);
12579         tpl.dom = node;
12580         // this has the unfortunate side effect of converting tagged attributes
12581         // eg. href="{...}" into %7C...%7D
12582         // this has been fixed by searching for those combo's although it's a bit hacky..
12583         
12584         
12585         tpl.body = div.innerHTML;
12586         
12587         
12588          
12589         tpl.id = tpl.uid;
12590         switch(tpl.attr) {
12591             case 'for' :
12592                 switch (tpl.value) {
12593                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12594                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12595                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12596                 }
12597                 break;
12598             
12599             case 'exec':
12600                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12601                 break;
12602             
12603             case 'if':     
12604                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12605                 break;
12606             
12607             case 'name':
12608                 tpl.id  = tpl.value; // replace non characters???
12609                 break;
12610             
12611         }
12612         
12613         
12614         this.tpls.push(tpl);
12615         
12616         
12617         
12618     },
12619     
12620     
12621     
12622     
12623     /**
12624      * Compile a segment of the template into a 'sub-template'
12625      *
12626      * 
12627      * 
12628      *
12629      */
12630     compileTpl : function(tpl)
12631     {
12632         var fm = Roo.util.Format;
12633         var useF = this.disableFormats !== true;
12634         
12635         var sep = Roo.isGecko ? "+\n" : ",\n";
12636         
12637         var undef = function(str) {
12638             Roo.debug && Roo.log("Property not found :"  + str);
12639             return '';
12640         };
12641           
12642         //Roo.log(tpl.body);
12643         
12644         
12645         
12646         var fn = function(m, lbrace, name, format, args)
12647         {
12648             //Roo.log("ARGS");
12649             //Roo.log(arguments);
12650             args = args ? args.replace(/\\'/g,"'") : args;
12651             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12652             if (typeof(format) == 'undefined') {
12653                 format =  'htmlEncode'; 
12654             }
12655             if (format == 'raw' ) {
12656                 format = false;
12657             }
12658             
12659             if(name.substr(0, 6) == 'domtpl'){
12660                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12661             }
12662             
12663             // build an array of options to determine if value is undefined..
12664             
12665             // basically get 'xxxx.yyyy' then do
12666             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12667             //    (function () { Roo.log("Property not found"); return ''; })() :
12668             //    ......
12669             
12670             var udef_ar = [];
12671             var lookfor = '';
12672             Roo.each(name.split('.'), function(st) {
12673                 lookfor += (lookfor.length ? '.': '') + st;
12674                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12675             });
12676             
12677             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12678             
12679             
12680             if(format && useF){
12681                 
12682                 args = args ? ',' + args : "";
12683                  
12684                 if(format.substr(0, 5) != "this."){
12685                     format = "fm." + format + '(';
12686                 }else{
12687                     format = 'this.call("'+ format.substr(5) + '", ';
12688                     args = ", values";
12689                 }
12690                 
12691                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12692             }
12693              
12694             if (args && args.length) {
12695                 // called with xxyx.yuu:(test,test)
12696                 // change to ()
12697                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12698             }
12699             // raw.. - :raw modifier..
12700             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12701             
12702         };
12703         var body;
12704         // branched to use + in gecko and [].join() in others
12705         if(Roo.isGecko){
12706             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12707                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12708                     "';};};";
12709         }else{
12710             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12711             body.push(tpl.body.replace(/(\r\n|\n)/g,
12712                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12713             body.push("'].join('');};};");
12714             body = body.join('');
12715         }
12716         
12717         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12718        
12719         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12720         eval(body);
12721         
12722         return this;
12723     },
12724      
12725     /**
12726      * same as applyTemplate, except it's done to one of the subTemplates
12727      * when using named templates, you can do:
12728      *
12729      * var str = pl.applySubTemplate('your-name', values);
12730      *
12731      * 
12732      * @param {Number} id of the template
12733      * @param {Object} values to apply to template
12734      * @param {Object} parent (normaly the instance of this object)
12735      */
12736     applySubTemplate : function(id, values, parent)
12737     {
12738         
12739         
12740         var t = this.tpls[id];
12741         
12742         
12743         try { 
12744             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12745                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12746                 return '';
12747             }
12748         } catch(e) {
12749             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12750             Roo.log(values);
12751           
12752             return '';
12753         }
12754         try { 
12755             
12756             if(t.execCall && t.execCall.call(this, values, parent)){
12757                 return '';
12758             }
12759         } catch(e) {
12760             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12761             Roo.log(values);
12762             return '';
12763         }
12764         
12765         try {
12766             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12767             parent = t.target ? values : parent;
12768             if(t.forCall && vs instanceof Array){
12769                 var buf = [];
12770                 for(var i = 0, len = vs.length; i < len; i++){
12771                     try {
12772                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12773                     } catch (e) {
12774                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12775                         Roo.log(e.body);
12776                         //Roo.log(t.compiled);
12777                         Roo.log(vs[i]);
12778                     }   
12779                 }
12780                 return buf.join('');
12781             }
12782         } catch (e) {
12783             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12784             Roo.log(values);
12785             return '';
12786         }
12787         try {
12788             return t.compiled.call(this, vs, parent);
12789         } catch (e) {
12790             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12791             Roo.log(e.body);
12792             //Roo.log(t.compiled);
12793             Roo.log(values);
12794             return '';
12795         }
12796     },
12797
12798    
12799
12800     applyTemplate : function(values){
12801         return this.master.compiled.call(this, values, {});
12802         //var s = this.subs;
12803     },
12804
12805     apply : function(){
12806         return this.applyTemplate.apply(this, arguments);
12807     }
12808
12809  });
12810
12811 Roo.DomTemplate.from = function(el){
12812     el = Roo.getDom(el);
12813     return new Roo.Domtemplate(el.value || el.innerHTML);
12814 };/*
12815  * Based on:
12816  * Ext JS Library 1.1.1
12817  * Copyright(c) 2006-2007, Ext JS, LLC.
12818  *
12819  * Originally Released Under LGPL - original licence link has changed is not relivant.
12820  *
12821  * Fork - LGPL
12822  * <script type="text/javascript">
12823  */
12824
12825 /**
12826  * @class Roo.util.DelayedTask
12827  * Provides a convenient method of performing setTimeout where a new
12828  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12829  * You can use this class to buffer
12830  * the keypress events for a certain number of milliseconds, and perform only if they stop
12831  * for that amount of time.
12832  * @constructor The parameters to this constructor serve as defaults and are not required.
12833  * @param {Function} fn (optional) The default function to timeout
12834  * @param {Object} scope (optional) The default scope of that timeout
12835  * @param {Array} args (optional) The default Array of arguments
12836  */
12837 Roo.util.DelayedTask = function(fn, scope, args){
12838     var id = null, d, t;
12839
12840     var call = function(){
12841         var now = new Date().getTime();
12842         if(now - t >= d){
12843             clearInterval(id);
12844             id = null;
12845             fn.apply(scope, args || []);
12846         }
12847     };
12848     /**
12849      * Cancels any pending timeout and queues a new one
12850      * @param {Number} delay The milliseconds to delay
12851      * @param {Function} newFn (optional) Overrides function passed to constructor
12852      * @param {Object} newScope (optional) Overrides scope passed to constructor
12853      * @param {Array} newArgs (optional) Overrides args passed to constructor
12854      */
12855     this.delay = function(delay, newFn, newScope, newArgs){
12856         if(id && delay != d){
12857             this.cancel();
12858         }
12859         d = delay;
12860         t = new Date().getTime();
12861         fn = newFn || fn;
12862         scope = newScope || scope;
12863         args = newArgs || args;
12864         if(!id){
12865             id = setInterval(call, d);
12866         }
12867     };
12868
12869     /**
12870      * Cancel the last queued timeout
12871      */
12872     this.cancel = function(){
12873         if(id){
12874             clearInterval(id);
12875             id = null;
12876         }
12877     };
12878 };/*
12879  * Based on:
12880  * Ext JS Library 1.1.1
12881  * Copyright(c) 2006-2007, Ext JS, LLC.
12882  *
12883  * Originally Released Under LGPL - original licence link has changed is not relivant.
12884  *
12885  * Fork - LGPL
12886  * <script type="text/javascript">
12887  */
12888  
12889  
12890 Roo.util.TaskRunner = function(interval){
12891     interval = interval || 10;
12892     var tasks = [], removeQueue = [];
12893     var id = 0;
12894     var running = false;
12895
12896     var stopThread = function(){
12897         running = false;
12898         clearInterval(id);
12899         id = 0;
12900     };
12901
12902     var startThread = function(){
12903         if(!running){
12904             running = true;
12905             id = setInterval(runTasks, interval);
12906         }
12907     };
12908
12909     var removeTask = function(task){
12910         removeQueue.push(task);
12911         if(task.onStop){
12912             task.onStop();
12913         }
12914     };
12915
12916     var runTasks = function(){
12917         if(removeQueue.length > 0){
12918             for(var i = 0, len = removeQueue.length; i < len; i++){
12919                 tasks.remove(removeQueue[i]);
12920             }
12921             removeQueue = [];
12922             if(tasks.length < 1){
12923                 stopThread();
12924                 return;
12925             }
12926         }
12927         var now = new Date().getTime();
12928         for(var i = 0, len = tasks.length; i < len; ++i){
12929             var t = tasks[i];
12930             var itime = now - t.taskRunTime;
12931             if(t.interval <= itime){
12932                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12933                 t.taskRunTime = now;
12934                 if(rt === false || t.taskRunCount === t.repeat){
12935                     removeTask(t);
12936                     return;
12937                 }
12938             }
12939             if(t.duration && t.duration <= (now - t.taskStartTime)){
12940                 removeTask(t);
12941             }
12942         }
12943     };
12944
12945     /**
12946      * Queues a new task.
12947      * @param {Object} task
12948      */
12949     this.start = function(task){
12950         tasks.push(task);
12951         task.taskStartTime = new Date().getTime();
12952         task.taskRunTime = 0;
12953         task.taskRunCount = 0;
12954         startThread();
12955         return task;
12956     };
12957
12958     this.stop = function(task){
12959         removeTask(task);
12960         return task;
12961     };
12962
12963     this.stopAll = function(){
12964         stopThread();
12965         for(var i = 0, len = tasks.length; i < len; i++){
12966             if(tasks[i].onStop){
12967                 tasks[i].onStop();
12968             }
12969         }
12970         tasks = [];
12971         removeQueue = [];
12972     };
12973 };
12974
12975 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12976  * Based on:
12977  * Ext JS Library 1.1.1
12978  * Copyright(c) 2006-2007, Ext JS, LLC.
12979  *
12980  * Originally Released Under LGPL - original licence link has changed is not relivant.
12981  *
12982  * Fork - LGPL
12983  * <script type="text/javascript">
12984  */
12985
12986  
12987 /**
12988  * @class Roo.util.MixedCollection
12989  * @extends Roo.util.Observable
12990  * A Collection class that maintains both numeric indexes and keys and exposes events.
12991  * @constructor
12992  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12993  * collection (defaults to false)
12994  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12995  * and return the key value for that item.  This is used when available to look up the key on items that
12996  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12997  * equivalent to providing an implementation for the {@link #getKey} method.
12998  */
12999 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13000     this.items = [];
13001     this.map = {};
13002     this.keys = [];
13003     this.length = 0;
13004     this.addEvents({
13005         /**
13006          * @event clear
13007          * Fires when the collection is cleared.
13008          */
13009         "clear" : true,
13010         /**
13011          * @event add
13012          * Fires when an item is added to the collection.
13013          * @param {Number} index The index at which the item was added.
13014          * @param {Object} o The item added.
13015          * @param {String} key The key associated with the added item.
13016          */
13017         "add" : true,
13018         /**
13019          * @event replace
13020          * Fires when an item is replaced in the collection.
13021          * @param {String} key he key associated with the new added.
13022          * @param {Object} old The item being replaced.
13023          * @param {Object} new The new item.
13024          */
13025         "replace" : true,
13026         /**
13027          * @event remove
13028          * Fires when an item is removed from the collection.
13029          * @param {Object} o The item being removed.
13030          * @param {String} key (optional) The key associated with the removed item.
13031          */
13032         "remove" : true,
13033         "sort" : true
13034     });
13035     this.allowFunctions = allowFunctions === true;
13036     if(keyFn){
13037         this.getKey = keyFn;
13038     }
13039     Roo.util.MixedCollection.superclass.constructor.call(this);
13040 };
13041
13042 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13043     allowFunctions : false,
13044     
13045 /**
13046  * Adds an item to the collection.
13047  * @param {String} key The key to associate with the item
13048  * @param {Object} o The item to add.
13049  * @return {Object} The item added.
13050  */
13051     add : function(key, o){
13052         if(arguments.length == 1){
13053             o = arguments[0];
13054             key = this.getKey(o);
13055         }
13056         if(typeof key == "undefined" || key === null){
13057             this.length++;
13058             this.items.push(o);
13059             this.keys.push(null);
13060         }else{
13061             var old = this.map[key];
13062             if(old){
13063                 return this.replace(key, o);
13064             }
13065             this.length++;
13066             this.items.push(o);
13067             this.map[key] = o;
13068             this.keys.push(key);
13069         }
13070         this.fireEvent("add", this.length-1, o, key);
13071         return o;
13072     },
13073        
13074 /**
13075   * MixedCollection has a generic way to fetch keys if you implement getKey.
13076 <pre><code>
13077 // normal way
13078 var mc = new Roo.util.MixedCollection();
13079 mc.add(someEl.dom.id, someEl);
13080 mc.add(otherEl.dom.id, otherEl);
13081 //and so on
13082
13083 // using getKey
13084 var mc = new Roo.util.MixedCollection();
13085 mc.getKey = function(el){
13086    return el.dom.id;
13087 };
13088 mc.add(someEl);
13089 mc.add(otherEl);
13090
13091 // or via the constructor
13092 var mc = new Roo.util.MixedCollection(false, function(el){
13093    return el.dom.id;
13094 });
13095 mc.add(someEl);
13096 mc.add(otherEl);
13097 </code></pre>
13098  * @param o {Object} The item for which to find the key.
13099  * @return {Object} The key for the passed item.
13100  */
13101     getKey : function(o){
13102          return o.id; 
13103     },
13104    
13105 /**
13106  * Replaces an item in the collection.
13107  * @param {String} key The key associated with the item to replace, or the item to replace.
13108  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13109  * @return {Object}  The new item.
13110  */
13111     replace : function(key, o){
13112         if(arguments.length == 1){
13113             o = arguments[0];
13114             key = this.getKey(o);
13115         }
13116         var old = this.item(key);
13117         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13118              return this.add(key, o);
13119         }
13120         var index = this.indexOfKey(key);
13121         this.items[index] = o;
13122         this.map[key] = o;
13123         this.fireEvent("replace", key, old, o);
13124         return o;
13125     },
13126    
13127 /**
13128  * Adds all elements of an Array or an Object to the collection.
13129  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13130  * an Array of values, each of which are added to the collection.
13131  */
13132     addAll : function(objs){
13133         if(arguments.length > 1 || objs instanceof Array){
13134             var args = arguments.length > 1 ? arguments : objs;
13135             for(var i = 0, len = args.length; i < len; i++){
13136                 this.add(args[i]);
13137             }
13138         }else{
13139             for(var key in objs){
13140                 if(this.allowFunctions || typeof objs[key] != "function"){
13141                     this.add(key, objs[key]);
13142                 }
13143             }
13144         }
13145     },
13146    
13147 /**
13148  * Executes the specified function once for every item in the collection, passing each
13149  * item as the first and only parameter. returning false from the function will stop the iteration.
13150  * @param {Function} fn The function to execute for each item.
13151  * @param {Object} scope (optional) The scope in which to execute the function.
13152  */
13153     each : function(fn, scope){
13154         var items = [].concat(this.items); // each safe for removal
13155         for(var i = 0, len = items.length; i < len; i++){
13156             if(fn.call(scope || items[i], items[i], i, len) === false){
13157                 break;
13158             }
13159         }
13160     },
13161    
13162 /**
13163  * Executes the specified function once for every key in the collection, passing each
13164  * key, and its associated item as the first two parameters.
13165  * @param {Function} fn The function to execute for each item.
13166  * @param {Object} scope (optional) The scope in which to execute the function.
13167  */
13168     eachKey : function(fn, scope){
13169         for(var i = 0, len = this.keys.length; i < len; i++){
13170             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13171         }
13172     },
13173    
13174 /**
13175  * Returns the first item in the collection which elicits a true return value from the
13176  * passed selection function.
13177  * @param {Function} fn The selection function to execute for each item.
13178  * @param {Object} scope (optional) The scope in which to execute the function.
13179  * @return {Object} The first item in the collection which returned true from the selection function.
13180  */
13181     find : function(fn, scope){
13182         for(var i = 0, len = this.items.length; i < len; i++){
13183             if(fn.call(scope || window, this.items[i], this.keys[i])){
13184                 return this.items[i];
13185             }
13186         }
13187         return null;
13188     },
13189    
13190 /**
13191  * Inserts an item at the specified index in the collection.
13192  * @param {Number} index The index to insert the item at.
13193  * @param {String} key The key to associate with the new item, or the item itself.
13194  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13195  * @return {Object} The item inserted.
13196  */
13197     insert : function(index, key, o){
13198         if(arguments.length == 2){
13199             o = arguments[1];
13200             key = this.getKey(o);
13201         }
13202         if(index >= this.length){
13203             return this.add(key, o);
13204         }
13205         this.length++;
13206         this.items.splice(index, 0, o);
13207         if(typeof key != "undefined" && key != null){
13208             this.map[key] = o;
13209         }
13210         this.keys.splice(index, 0, key);
13211         this.fireEvent("add", index, o, key);
13212         return o;
13213     },
13214    
13215 /**
13216  * Removed an item from the collection.
13217  * @param {Object} o The item to remove.
13218  * @return {Object} The item removed.
13219  */
13220     remove : function(o){
13221         return this.removeAt(this.indexOf(o));
13222     },
13223    
13224 /**
13225  * Remove an item from a specified index in the collection.
13226  * @param {Number} index The index within the collection of the item to remove.
13227  */
13228     removeAt : function(index){
13229         if(index < this.length && index >= 0){
13230             this.length--;
13231             var o = this.items[index];
13232             this.items.splice(index, 1);
13233             var key = this.keys[index];
13234             if(typeof key != "undefined"){
13235                 delete this.map[key];
13236             }
13237             this.keys.splice(index, 1);
13238             this.fireEvent("remove", o, key);
13239         }
13240     },
13241    
13242 /**
13243  * Removed an item associated with the passed key fom the collection.
13244  * @param {String} key The key of the item to remove.
13245  */
13246     removeKey : function(key){
13247         return this.removeAt(this.indexOfKey(key));
13248     },
13249    
13250 /**
13251  * Returns the number of items in the collection.
13252  * @return {Number} the number of items in the collection.
13253  */
13254     getCount : function(){
13255         return this.length; 
13256     },
13257    
13258 /**
13259  * Returns index within the collection of the passed Object.
13260  * @param {Object} o The item to find the index of.
13261  * @return {Number} index of the item.
13262  */
13263     indexOf : function(o){
13264         if(!this.items.indexOf){
13265             for(var i = 0, len = this.items.length; i < len; i++){
13266                 if(this.items[i] == o) {
13267                     return i;
13268                 }
13269             }
13270             return -1;
13271         }else{
13272             return this.items.indexOf(o);
13273         }
13274     },
13275    
13276 /**
13277  * Returns index within the collection of the passed key.
13278  * @param {String} key The key to find the index of.
13279  * @return {Number} index of the key.
13280  */
13281     indexOfKey : function(key){
13282         if(!this.keys.indexOf){
13283             for(var i = 0, len = this.keys.length; i < len; i++){
13284                 if(this.keys[i] == key) {
13285                     return i;
13286                 }
13287             }
13288             return -1;
13289         }else{
13290             return this.keys.indexOf(key);
13291         }
13292     },
13293    
13294 /**
13295  * Returns the item associated with the passed key OR index. Key has priority over index.
13296  * @param {String/Number} key The key or index of the item.
13297  * @return {Object} The item associated with the passed key.
13298  */
13299     item : function(key){
13300         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13301         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13302     },
13303     
13304 /**
13305  * Returns the item at the specified index.
13306  * @param {Number} index The index of the item.
13307  * @return {Object}
13308  */
13309     itemAt : function(index){
13310         return this.items[index];
13311     },
13312     
13313 /**
13314  * Returns the item associated with the passed key.
13315  * @param {String/Number} key The key of the item.
13316  * @return {Object} The item associated with the passed key.
13317  */
13318     key : function(key){
13319         return this.map[key];
13320     },
13321    
13322 /**
13323  * Returns true if the collection contains the passed Object as an item.
13324  * @param {Object} o  The Object to look for in the collection.
13325  * @return {Boolean} True if the collection contains the Object as an item.
13326  */
13327     contains : function(o){
13328         return this.indexOf(o) != -1;
13329     },
13330    
13331 /**
13332  * Returns true if the collection contains the passed Object as a key.
13333  * @param {String} key The key to look for in the collection.
13334  * @return {Boolean} True if the collection contains the Object as a key.
13335  */
13336     containsKey : function(key){
13337         return typeof this.map[key] != "undefined";
13338     },
13339    
13340 /**
13341  * Removes all items from the collection.
13342  */
13343     clear : function(){
13344         this.length = 0;
13345         this.items = [];
13346         this.keys = [];
13347         this.map = {};
13348         this.fireEvent("clear");
13349     },
13350    
13351 /**
13352  * Returns the first item in the collection.
13353  * @return {Object} the first item in the collection..
13354  */
13355     first : function(){
13356         return this.items[0]; 
13357     },
13358    
13359 /**
13360  * Returns the last item in the collection.
13361  * @return {Object} the last item in the collection..
13362  */
13363     last : function(){
13364         return this.items[this.length-1];   
13365     },
13366     
13367     _sort : function(property, dir, fn){
13368         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13369         fn = fn || function(a, b){
13370             return a-b;
13371         };
13372         var c = [], k = this.keys, items = this.items;
13373         for(var i = 0, len = items.length; i < len; i++){
13374             c[c.length] = {key: k[i], value: items[i], index: i};
13375         }
13376         c.sort(function(a, b){
13377             var v = fn(a[property], b[property]) * dsc;
13378             if(v == 0){
13379                 v = (a.index < b.index ? -1 : 1);
13380             }
13381             return v;
13382         });
13383         for(var i = 0, len = c.length; i < len; i++){
13384             items[i] = c[i].value;
13385             k[i] = c[i].key;
13386         }
13387         this.fireEvent("sort", this);
13388     },
13389     
13390     /**
13391      * Sorts this collection with the passed comparison function
13392      * @param {String} direction (optional) "ASC" or "DESC"
13393      * @param {Function} fn (optional) comparison function
13394      */
13395     sort : function(dir, fn){
13396         this._sort("value", dir, fn);
13397     },
13398     
13399     /**
13400      * Sorts this collection by keys
13401      * @param {String} direction (optional) "ASC" or "DESC"
13402      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13403      */
13404     keySort : function(dir, fn){
13405         this._sort("key", dir, fn || function(a, b){
13406             return String(a).toUpperCase()-String(b).toUpperCase();
13407         });
13408     },
13409     
13410     /**
13411      * Returns a range of items in this collection
13412      * @param {Number} startIndex (optional) defaults to 0
13413      * @param {Number} endIndex (optional) default to the last item
13414      * @return {Array} An array of items
13415      */
13416     getRange : function(start, end){
13417         var items = this.items;
13418         if(items.length < 1){
13419             return [];
13420         }
13421         start = start || 0;
13422         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13423         var r = [];
13424         if(start <= end){
13425             for(var i = start; i <= end; i++) {
13426                     r[r.length] = items[i];
13427             }
13428         }else{
13429             for(var i = start; i >= end; i--) {
13430                     r[r.length] = items[i];
13431             }
13432         }
13433         return r;
13434     },
13435         
13436     /**
13437      * Filter the <i>objects</i> in this collection by a specific property. 
13438      * Returns a new collection that has been filtered.
13439      * @param {String} property A property on your objects
13440      * @param {String/RegExp} value Either string that the property values 
13441      * should start with or a RegExp to test against the property
13442      * @return {MixedCollection} The new filtered collection
13443      */
13444     filter : function(property, value){
13445         if(!value.exec){ // not a regex
13446             value = String(value);
13447             if(value.length == 0){
13448                 return this.clone();
13449             }
13450             value = new RegExp("^" + Roo.escapeRe(value), "i");
13451         }
13452         return this.filterBy(function(o){
13453             return o && value.test(o[property]);
13454         });
13455         },
13456     
13457     /**
13458      * Filter by a function. * Returns a new collection that has been filtered.
13459      * The passed function will be called with each 
13460      * object in the collection. If the function returns true, the value is included 
13461      * otherwise it is filtered.
13462      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13463      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13464      * @return {MixedCollection} The new filtered collection
13465      */
13466     filterBy : function(fn, scope){
13467         var r = new Roo.util.MixedCollection();
13468         r.getKey = this.getKey;
13469         var k = this.keys, it = this.items;
13470         for(var i = 0, len = it.length; i < len; i++){
13471             if(fn.call(scope||this, it[i], k[i])){
13472                                 r.add(k[i], it[i]);
13473                         }
13474         }
13475         return r;
13476     },
13477     
13478     /**
13479      * Creates a duplicate of this collection
13480      * @return {MixedCollection}
13481      */
13482     clone : function(){
13483         var r = new Roo.util.MixedCollection();
13484         var k = this.keys, it = this.items;
13485         for(var i = 0, len = it.length; i < len; i++){
13486             r.add(k[i], it[i]);
13487         }
13488         r.getKey = this.getKey;
13489         return r;
13490     }
13491 });
13492 /**
13493  * Returns the item associated with the passed key or index.
13494  * @method
13495  * @param {String/Number} key The key or index of the item.
13496  * @return {Object} The item associated with the passed key.
13497  */
13498 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13499  * Based on:
13500  * Ext JS Library 1.1.1
13501  * Copyright(c) 2006-2007, Ext JS, LLC.
13502  *
13503  * Originally Released Under LGPL - original licence link has changed is not relivant.
13504  *
13505  * Fork - LGPL
13506  * <script type="text/javascript">
13507  */
13508 /**
13509  * @class Roo.util.JSON
13510  * Modified version of Douglas Crockford"s json.js that doesn"t
13511  * mess with the Object prototype 
13512  * http://www.json.org/js.html
13513  * @singleton
13514  */
13515 Roo.util.JSON = new (function(){
13516     var useHasOwn = {}.hasOwnProperty ? true : false;
13517     
13518     // crashes Safari in some instances
13519     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13520     
13521     var pad = function(n) {
13522         return n < 10 ? "0" + n : n;
13523     };
13524     
13525     var m = {
13526         "\b": '\\b',
13527         "\t": '\\t',
13528         "\n": '\\n',
13529         "\f": '\\f',
13530         "\r": '\\r',
13531         '"' : '\\"',
13532         "\\": '\\\\'
13533     };
13534
13535     var encodeString = function(s){
13536         if (/["\\\x00-\x1f]/.test(s)) {
13537             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13538                 var c = m[b];
13539                 if(c){
13540                     return c;
13541                 }
13542                 c = b.charCodeAt();
13543                 return "\\u00" +
13544                     Math.floor(c / 16).toString(16) +
13545                     (c % 16).toString(16);
13546             }) + '"';
13547         }
13548         return '"' + s + '"';
13549     };
13550     
13551     var encodeArray = function(o){
13552         var a = ["["], b, i, l = o.length, v;
13553             for (i = 0; i < l; i += 1) {
13554                 v = o[i];
13555                 switch (typeof v) {
13556                     case "undefined":
13557                     case "function":
13558                     case "unknown":
13559                         break;
13560                     default:
13561                         if (b) {
13562                             a.push(',');
13563                         }
13564                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13565                         b = true;
13566                 }
13567             }
13568             a.push("]");
13569             return a.join("");
13570     };
13571     
13572     var encodeDate = function(o){
13573         return '"' + o.getFullYear() + "-" +
13574                 pad(o.getMonth() + 1) + "-" +
13575                 pad(o.getDate()) + "T" +
13576                 pad(o.getHours()) + ":" +
13577                 pad(o.getMinutes()) + ":" +
13578                 pad(o.getSeconds()) + '"';
13579     };
13580     
13581     /**
13582      * Encodes an Object, Array or other value
13583      * @param {Mixed} o The variable to encode
13584      * @return {String} The JSON string
13585      */
13586     this.encode = function(o)
13587     {
13588         // should this be extended to fully wrap stringify..
13589         
13590         if(typeof o == "undefined" || o === null){
13591             return "null";
13592         }else if(o instanceof Array){
13593             return encodeArray(o);
13594         }else if(o instanceof Date){
13595             return encodeDate(o);
13596         }else if(typeof o == "string"){
13597             return encodeString(o);
13598         }else if(typeof o == "number"){
13599             return isFinite(o) ? String(o) : "null";
13600         }else if(typeof o == "boolean"){
13601             return String(o);
13602         }else {
13603             var a = ["{"], b, i, v;
13604             for (i in o) {
13605                 if(!useHasOwn || o.hasOwnProperty(i)) {
13606                     v = o[i];
13607                     switch (typeof v) {
13608                     case "undefined":
13609                     case "function":
13610                     case "unknown":
13611                         break;
13612                     default:
13613                         if(b){
13614                             a.push(',');
13615                         }
13616                         a.push(this.encode(i), ":",
13617                                 v === null ? "null" : this.encode(v));
13618                         b = true;
13619                     }
13620                 }
13621             }
13622             a.push("}");
13623             return a.join("");
13624         }
13625     };
13626     
13627     /**
13628      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13629      * @param {String} json The JSON string
13630      * @return {Object} The resulting object
13631      */
13632     this.decode = function(json){
13633         
13634         return  /** eval:var:json */ eval("(" + json + ')');
13635     };
13636 })();
13637 /** 
13638  * Shorthand for {@link Roo.util.JSON#encode}
13639  * @member Roo encode 
13640  * @method */
13641 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13642 /** 
13643  * Shorthand for {@link Roo.util.JSON#decode}
13644  * @member Roo decode 
13645  * @method */
13646 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13647 /*
13648  * Based on:
13649  * Ext JS Library 1.1.1
13650  * Copyright(c) 2006-2007, Ext JS, LLC.
13651  *
13652  * Originally Released Under LGPL - original licence link has changed is not relivant.
13653  *
13654  * Fork - LGPL
13655  * <script type="text/javascript">
13656  */
13657  
13658 /**
13659  * @class Roo.util.Format
13660  * Reusable data formatting functions
13661  * @singleton
13662  */
13663 Roo.util.Format = function(){
13664     var trimRe = /^\s+|\s+$/g;
13665     return {
13666         /**
13667          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13668          * @param {String} value The string to truncate
13669          * @param {Number} length The maximum length to allow before truncating
13670          * @return {String} The converted text
13671          */
13672         ellipsis : function(value, len){
13673             if(value && value.length > len){
13674                 return value.substr(0, len-3)+"...";
13675             }
13676             return value;
13677         },
13678
13679         /**
13680          * Checks a reference and converts it to empty string if it is undefined
13681          * @param {Mixed} value Reference to check
13682          * @return {Mixed} Empty string if converted, otherwise the original value
13683          */
13684         undef : function(value){
13685             return typeof value != "undefined" ? value : "";
13686         },
13687
13688         /**
13689          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13690          * @param {String} value The string to encode
13691          * @return {String} The encoded text
13692          */
13693         htmlEncode : function(value){
13694             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13695         },
13696
13697         /**
13698          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13699          * @param {String} value The string to decode
13700          * @return {String} The decoded text
13701          */
13702         htmlDecode : function(value){
13703             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13704         },
13705
13706         /**
13707          * Trims any whitespace from either side of a string
13708          * @param {String} value The text to trim
13709          * @return {String} The trimmed text
13710          */
13711         trim : function(value){
13712             return String(value).replace(trimRe, "");
13713         },
13714
13715         /**
13716          * Returns a substring from within an original string
13717          * @param {String} value The original text
13718          * @param {Number} start The start index of the substring
13719          * @param {Number} length The length of the substring
13720          * @return {String} The substring
13721          */
13722         substr : function(value, start, length){
13723             return String(value).substr(start, length);
13724         },
13725
13726         /**
13727          * Converts a string to all lower case letters
13728          * @param {String} value The text to convert
13729          * @return {String} The converted text
13730          */
13731         lowercase : function(value){
13732             return String(value).toLowerCase();
13733         },
13734
13735         /**
13736          * Converts a string to all upper case letters
13737          * @param {String} value The text to convert
13738          * @return {String} The converted text
13739          */
13740         uppercase : function(value){
13741             return String(value).toUpperCase();
13742         },
13743
13744         /**
13745          * Converts the first character only of a string to upper case
13746          * @param {String} value The text to convert
13747          * @return {String} The converted text
13748          */
13749         capitalize : function(value){
13750             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13751         },
13752
13753         // private
13754         call : function(value, fn){
13755             if(arguments.length > 2){
13756                 var args = Array.prototype.slice.call(arguments, 2);
13757                 args.unshift(value);
13758                  
13759                 return /** eval:var:value */  eval(fn).apply(window, args);
13760             }else{
13761                 /** eval:var:value */
13762                 return /** eval:var:value */ eval(fn).call(window, value);
13763             }
13764         },
13765
13766        
13767         /**
13768          * safer version of Math.toFixed..??/
13769          * @param {Number/String} value The numeric value to format
13770          * @param {Number/String} value Decimal places 
13771          * @return {String} The formatted currency string
13772          */
13773         toFixed : function(v, n)
13774         {
13775             // why not use to fixed - precision is buggered???
13776             if (!n) {
13777                 return Math.round(v-0);
13778             }
13779             var fact = Math.pow(10,n+1);
13780             v = (Math.round((v-0)*fact))/fact;
13781             var z = (''+fact).substring(2);
13782             if (v == Math.floor(v)) {
13783                 return Math.floor(v) + '.' + z;
13784             }
13785             
13786             // now just padd decimals..
13787             var ps = String(v).split('.');
13788             var fd = (ps[1] + z);
13789             var r = fd.substring(0,n); 
13790             var rm = fd.substring(n); 
13791             if (rm < 5) {
13792                 return ps[0] + '.' + r;
13793             }
13794             r*=1; // turn it into a number;
13795             r++;
13796             if (String(r).length != n) {
13797                 ps[0]*=1;
13798                 ps[0]++;
13799                 r = String(r).substring(1); // chop the end off.
13800             }
13801             
13802             return ps[0] + '.' + r;
13803              
13804         },
13805         
13806         /**
13807          * Format a number as US currency
13808          * @param {Number/String} value The numeric value to format
13809          * @return {String} The formatted currency string
13810          */
13811         usMoney : function(v){
13812             return '$' + Roo.util.Format.number(v);
13813         },
13814         
13815         /**
13816          * Format a number
13817          * eventually this should probably emulate php's number_format
13818          * @param {Number/String} value The numeric value to format
13819          * @param {Number} decimals number of decimal places
13820          * @param {String} delimiter for thousands (default comma)
13821          * @return {String} The formatted currency string
13822          */
13823         number : function(v, decimals, thousandsDelimiter)
13824         {
13825             // multiply and round.
13826             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13827             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13828             
13829             var mul = Math.pow(10, decimals);
13830             var zero = String(mul).substring(1);
13831             v = (Math.round((v-0)*mul))/mul;
13832             
13833             // if it's '0' number.. then
13834             
13835             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13836             v = String(v);
13837             var ps = v.split('.');
13838             var whole = ps[0];
13839             
13840             var r = /(\d+)(\d{3})/;
13841             // add comma's
13842             
13843             if(thousandsDelimiter.length != 0) {
13844                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13845             } 
13846             
13847             var sub = ps[1] ?
13848                     // has decimals..
13849                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13850                     // does not have decimals
13851                     (decimals ? ('.' + zero) : '');
13852             
13853             
13854             return whole + sub ;
13855         },
13856         
13857         /**
13858          * Parse a value into a formatted date using the specified format pattern.
13859          * @param {Mixed} value The value to format
13860          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13861          * @return {String} The formatted date string
13862          */
13863         date : function(v, format){
13864             if(!v){
13865                 return "";
13866             }
13867             if(!(v instanceof Date)){
13868                 v = new Date(Date.parse(v));
13869             }
13870             return v.dateFormat(format || Roo.util.Format.defaults.date);
13871         },
13872
13873         /**
13874          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13875          * @param {String} format Any valid date format string
13876          * @return {Function} The date formatting function
13877          */
13878         dateRenderer : function(format){
13879             return function(v){
13880                 return Roo.util.Format.date(v, format);  
13881             };
13882         },
13883
13884         // private
13885         stripTagsRE : /<\/?[^>]+>/gi,
13886         
13887         /**
13888          * Strips all HTML tags
13889          * @param {Mixed} value The text from which to strip tags
13890          * @return {String} The stripped text
13891          */
13892         stripTags : function(v){
13893             return !v ? v : String(v).replace(this.stripTagsRE, "");
13894         }
13895     };
13896 }();
13897 Roo.util.Format.defaults = {
13898     date : 'd/M/Y'
13899 };/*
13900  * Based on:
13901  * Ext JS Library 1.1.1
13902  * Copyright(c) 2006-2007, Ext JS, LLC.
13903  *
13904  * Originally Released Under LGPL - original licence link has changed is not relivant.
13905  *
13906  * Fork - LGPL
13907  * <script type="text/javascript">
13908  */
13909
13910
13911  
13912
13913 /**
13914  * @class Roo.MasterTemplate
13915  * @extends Roo.Template
13916  * Provides a template that can have child templates. The syntax is:
13917 <pre><code>
13918 var t = new Roo.MasterTemplate(
13919         '&lt;select name="{name}"&gt;',
13920                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13921         '&lt;/select&gt;'
13922 );
13923 t.add('options', {value: 'foo', text: 'bar'});
13924 // or you can add multiple child elements in one shot
13925 t.addAll('options', [
13926     {value: 'foo', text: 'bar'},
13927     {value: 'foo2', text: 'bar2'},
13928     {value: 'foo3', text: 'bar3'}
13929 ]);
13930 // then append, applying the master template values
13931 t.append('my-form', {name: 'my-select'});
13932 </code></pre>
13933 * A name attribute for the child template is not required if you have only one child
13934 * template or you want to refer to them by index.
13935  */
13936 Roo.MasterTemplate = function(){
13937     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13938     this.originalHtml = this.html;
13939     var st = {};
13940     var m, re = this.subTemplateRe;
13941     re.lastIndex = 0;
13942     var subIndex = 0;
13943     while(m = re.exec(this.html)){
13944         var name = m[1], content = m[2];
13945         st[subIndex] = {
13946             name: name,
13947             index: subIndex,
13948             buffer: [],
13949             tpl : new Roo.Template(content)
13950         };
13951         if(name){
13952             st[name] = st[subIndex];
13953         }
13954         st[subIndex].tpl.compile();
13955         st[subIndex].tpl.call = this.call.createDelegate(this);
13956         subIndex++;
13957     }
13958     this.subCount = subIndex;
13959     this.subs = st;
13960 };
13961 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13962     /**
13963     * The regular expression used to match sub templates
13964     * @type RegExp
13965     * @property
13966     */
13967     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13968
13969     /**
13970      * Applies the passed values to a child template.
13971      * @param {String/Number} name (optional) The name or index of the child template
13972      * @param {Array/Object} values The values to be applied to the template
13973      * @return {MasterTemplate} this
13974      */
13975      add : function(name, values){
13976         if(arguments.length == 1){
13977             values = arguments[0];
13978             name = 0;
13979         }
13980         var s = this.subs[name];
13981         s.buffer[s.buffer.length] = s.tpl.apply(values);
13982         return this;
13983     },
13984
13985     /**
13986      * Applies all the passed values to a child template.
13987      * @param {String/Number} name (optional) The name or index of the child template
13988      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13989      * @param {Boolean} reset (optional) True to reset the template first
13990      * @return {MasterTemplate} this
13991      */
13992     fill : function(name, values, reset){
13993         var a = arguments;
13994         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13995             values = a[0];
13996             name = 0;
13997             reset = a[1];
13998         }
13999         if(reset){
14000             this.reset();
14001         }
14002         for(var i = 0, len = values.length; i < len; i++){
14003             this.add(name, values[i]);
14004         }
14005         return this;
14006     },
14007
14008     /**
14009      * Resets the template for reuse
14010      * @return {MasterTemplate} this
14011      */
14012      reset : function(){
14013         var s = this.subs;
14014         for(var i = 0; i < this.subCount; i++){
14015             s[i].buffer = [];
14016         }
14017         return this;
14018     },
14019
14020     applyTemplate : function(values){
14021         var s = this.subs;
14022         var replaceIndex = -1;
14023         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14024             return s[++replaceIndex].buffer.join("");
14025         });
14026         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14027     },
14028
14029     apply : function(){
14030         return this.applyTemplate.apply(this, arguments);
14031     },
14032
14033     compile : function(){return this;}
14034 });
14035
14036 /**
14037  * Alias for fill().
14038  * @method
14039  */
14040 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14041  /**
14042  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14043  * var tpl = Roo.MasterTemplate.from('element-id');
14044  * @param {String/HTMLElement} el
14045  * @param {Object} config
14046  * @static
14047  */
14048 Roo.MasterTemplate.from = function(el, config){
14049     el = Roo.getDom(el);
14050     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14051 };/*
14052  * Based on:
14053  * Ext JS Library 1.1.1
14054  * Copyright(c) 2006-2007, Ext JS, LLC.
14055  *
14056  * Originally Released Under LGPL - original licence link has changed is not relivant.
14057  *
14058  * Fork - LGPL
14059  * <script type="text/javascript">
14060  */
14061
14062  
14063 /**
14064  * @class Roo.util.CSS
14065  * Utility class for manipulating CSS rules
14066  * @singleton
14067  */
14068 Roo.util.CSS = function(){
14069         var rules = null;
14070         var doc = document;
14071
14072     var camelRe = /(-[a-z])/gi;
14073     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14074
14075    return {
14076    /**
14077     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14078     * tag and appended to the HEAD of the document.
14079     * @param {String|Object} cssText The text containing the css rules
14080     * @param {String} id An id to add to the stylesheet for later removal
14081     * @return {StyleSheet}
14082     */
14083     createStyleSheet : function(cssText, id){
14084         var ss;
14085         var head = doc.getElementsByTagName("head")[0];
14086         var nrules = doc.createElement("style");
14087         nrules.setAttribute("type", "text/css");
14088         if(id){
14089             nrules.setAttribute("id", id);
14090         }
14091         if (typeof(cssText) != 'string') {
14092             // support object maps..
14093             // not sure if this a good idea.. 
14094             // perhaps it should be merged with the general css handling
14095             // and handle js style props.
14096             var cssTextNew = [];
14097             for(var n in cssText) {
14098                 var citems = [];
14099                 for(var k in cssText[n]) {
14100                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14101                 }
14102                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14103                 
14104             }
14105             cssText = cssTextNew.join("\n");
14106             
14107         }
14108        
14109        
14110        if(Roo.isIE){
14111            head.appendChild(nrules);
14112            ss = nrules.styleSheet;
14113            ss.cssText = cssText;
14114        }else{
14115            try{
14116                 nrules.appendChild(doc.createTextNode(cssText));
14117            }catch(e){
14118                nrules.cssText = cssText; 
14119            }
14120            head.appendChild(nrules);
14121            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14122        }
14123        this.cacheStyleSheet(ss);
14124        return ss;
14125    },
14126
14127    /**
14128     * Removes a style or link tag by id
14129     * @param {String} id The id of the tag
14130     */
14131    removeStyleSheet : function(id){
14132        var existing = doc.getElementById(id);
14133        if(existing){
14134            existing.parentNode.removeChild(existing);
14135        }
14136    },
14137
14138    /**
14139     * Dynamically swaps an existing stylesheet reference for a new one
14140     * @param {String} id The id of an existing link tag to remove
14141     * @param {String} url The href of the new stylesheet to include
14142     */
14143    swapStyleSheet : function(id, url){
14144        this.removeStyleSheet(id);
14145        var ss = doc.createElement("link");
14146        ss.setAttribute("rel", "stylesheet");
14147        ss.setAttribute("type", "text/css");
14148        ss.setAttribute("id", id);
14149        ss.setAttribute("href", url);
14150        doc.getElementsByTagName("head")[0].appendChild(ss);
14151    },
14152    
14153    /**
14154     * Refresh the rule cache if you have dynamically added stylesheets
14155     * @return {Object} An object (hash) of rules indexed by selector
14156     */
14157    refreshCache : function(){
14158        return this.getRules(true);
14159    },
14160
14161    // private
14162    cacheStyleSheet : function(stylesheet){
14163        if(!rules){
14164            rules = {};
14165        }
14166        try{// try catch for cross domain access issue
14167            var ssRules = stylesheet.cssRules || stylesheet.rules;
14168            for(var j = ssRules.length-1; j >= 0; --j){
14169                rules[ssRules[j].selectorText] = ssRules[j];
14170            }
14171        }catch(e){}
14172    },
14173    
14174    /**
14175     * Gets all css rules for the document
14176     * @param {Boolean} refreshCache true to refresh the internal cache
14177     * @return {Object} An object (hash) of rules indexed by selector
14178     */
14179    getRules : function(refreshCache){
14180                 if(rules == null || refreshCache){
14181                         rules = {};
14182                         var ds = doc.styleSheets;
14183                         for(var i =0, len = ds.length; i < len; i++){
14184                             try{
14185                         this.cacheStyleSheet(ds[i]);
14186                     }catch(e){} 
14187                 }
14188                 }
14189                 return rules;
14190         },
14191         
14192         /**
14193     * Gets an an individual CSS rule by selector(s)
14194     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14195     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14196     * @return {CSSRule} The CSS rule or null if one is not found
14197     */
14198    getRule : function(selector, refreshCache){
14199                 var rs = this.getRules(refreshCache);
14200                 if(!(selector instanceof Array)){
14201                     return rs[selector];
14202                 }
14203                 for(var i = 0; i < selector.length; i++){
14204                         if(rs[selector[i]]){
14205                                 return rs[selector[i]];
14206                         }
14207                 }
14208                 return null;
14209         },
14210         
14211         
14212         /**
14213     * Updates a rule property
14214     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14215     * @param {String} property The css property
14216     * @param {String} value The new value for the property
14217     * @return {Boolean} true If a rule was found and updated
14218     */
14219    updateRule : function(selector, property, value){
14220                 if(!(selector instanceof Array)){
14221                         var rule = this.getRule(selector);
14222                         if(rule){
14223                                 rule.style[property.replace(camelRe, camelFn)] = value;
14224                                 return true;
14225                         }
14226                 }else{
14227                         for(var i = 0; i < selector.length; i++){
14228                                 if(this.updateRule(selector[i], property, value)){
14229                                         return true;
14230                                 }
14231                         }
14232                 }
14233                 return false;
14234         }
14235    };   
14236 }();/*
14237  * Based on:
14238  * Ext JS Library 1.1.1
14239  * Copyright(c) 2006-2007, Ext JS, LLC.
14240  *
14241  * Originally Released Under LGPL - original licence link has changed is not relivant.
14242  *
14243  * Fork - LGPL
14244  * <script type="text/javascript">
14245  */
14246
14247  
14248
14249 /**
14250  * @class Roo.util.ClickRepeater
14251  * @extends Roo.util.Observable
14252  * 
14253  * A wrapper class which can be applied to any element. Fires a "click" event while the
14254  * mouse is pressed. The interval between firings may be specified in the config but
14255  * defaults to 10 milliseconds.
14256  * 
14257  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14258  * 
14259  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14260  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14261  * Similar to an autorepeat key delay.
14262  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14263  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14264  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14265  *           "interval" and "delay" are ignored. "immediate" is honored.
14266  * @cfg {Boolean} preventDefault True to prevent the default click event
14267  * @cfg {Boolean} stopDefault True to stop the default click event
14268  * 
14269  * @history
14270  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14271  *     2007-02-02 jvs Renamed to ClickRepeater
14272  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14273  *
14274  *  @constructor
14275  * @param {String/HTMLElement/Element} el The element to listen on
14276  * @param {Object} config
14277  **/
14278 Roo.util.ClickRepeater = function(el, config)
14279 {
14280     this.el = Roo.get(el);
14281     this.el.unselectable();
14282
14283     Roo.apply(this, config);
14284
14285     this.addEvents({
14286     /**
14287      * @event mousedown
14288      * Fires when the mouse button is depressed.
14289      * @param {Roo.util.ClickRepeater} this
14290      */
14291         "mousedown" : true,
14292     /**
14293      * @event click
14294      * Fires on a specified interval during the time the element is pressed.
14295      * @param {Roo.util.ClickRepeater} this
14296      */
14297         "click" : true,
14298     /**
14299      * @event mouseup
14300      * Fires when the mouse key is released.
14301      * @param {Roo.util.ClickRepeater} this
14302      */
14303         "mouseup" : true
14304     });
14305
14306     this.el.on("mousedown", this.handleMouseDown, this);
14307     if(this.preventDefault || this.stopDefault){
14308         this.el.on("click", function(e){
14309             if(this.preventDefault){
14310                 e.preventDefault();
14311             }
14312             if(this.stopDefault){
14313                 e.stopEvent();
14314             }
14315         }, this);
14316     }
14317
14318     // allow inline handler
14319     if(this.handler){
14320         this.on("click", this.handler,  this.scope || this);
14321     }
14322
14323     Roo.util.ClickRepeater.superclass.constructor.call(this);
14324 };
14325
14326 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14327     interval : 20,
14328     delay: 250,
14329     preventDefault : true,
14330     stopDefault : false,
14331     timer : 0,
14332
14333     // private
14334     handleMouseDown : function(){
14335         clearTimeout(this.timer);
14336         this.el.blur();
14337         if(this.pressClass){
14338             this.el.addClass(this.pressClass);
14339         }
14340         this.mousedownTime = new Date();
14341
14342         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14343         this.el.on("mouseout", this.handleMouseOut, this);
14344
14345         this.fireEvent("mousedown", this);
14346         this.fireEvent("click", this);
14347         
14348         this.timer = this.click.defer(this.delay || this.interval, this);
14349     },
14350
14351     // private
14352     click : function(){
14353         this.fireEvent("click", this);
14354         this.timer = this.click.defer(this.getInterval(), this);
14355     },
14356
14357     // private
14358     getInterval: function(){
14359         if(!this.accelerate){
14360             return this.interval;
14361         }
14362         var pressTime = this.mousedownTime.getElapsed();
14363         if(pressTime < 500){
14364             return 400;
14365         }else if(pressTime < 1700){
14366             return 320;
14367         }else if(pressTime < 2600){
14368             return 250;
14369         }else if(pressTime < 3500){
14370             return 180;
14371         }else if(pressTime < 4400){
14372             return 140;
14373         }else if(pressTime < 5300){
14374             return 80;
14375         }else if(pressTime < 6200){
14376             return 50;
14377         }else{
14378             return 10;
14379         }
14380     },
14381
14382     // private
14383     handleMouseOut : function(){
14384         clearTimeout(this.timer);
14385         if(this.pressClass){
14386             this.el.removeClass(this.pressClass);
14387         }
14388         this.el.on("mouseover", this.handleMouseReturn, this);
14389     },
14390
14391     // private
14392     handleMouseReturn : function(){
14393         this.el.un("mouseover", this.handleMouseReturn);
14394         if(this.pressClass){
14395             this.el.addClass(this.pressClass);
14396         }
14397         this.click();
14398     },
14399
14400     // private
14401     handleMouseUp : function(){
14402         clearTimeout(this.timer);
14403         this.el.un("mouseover", this.handleMouseReturn);
14404         this.el.un("mouseout", this.handleMouseOut);
14405         Roo.get(document).un("mouseup", this.handleMouseUp);
14406         this.el.removeClass(this.pressClass);
14407         this.fireEvent("mouseup", this);
14408     }
14409 });/*
14410  * Based on:
14411  * Ext JS Library 1.1.1
14412  * Copyright(c) 2006-2007, Ext JS, LLC.
14413  *
14414  * Originally Released Under LGPL - original licence link has changed is not relivant.
14415  *
14416  * Fork - LGPL
14417  * <script type="text/javascript">
14418  */
14419
14420  
14421 /**
14422  * @class Roo.KeyNav
14423  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14424  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14425  * way to implement custom navigation schemes for any UI component.</p>
14426  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14427  * pageUp, pageDown, del, home, end.  Usage:</p>
14428  <pre><code>
14429 var nav = new Roo.KeyNav("my-element", {
14430     "left" : function(e){
14431         this.moveLeft(e.ctrlKey);
14432     },
14433     "right" : function(e){
14434         this.moveRight(e.ctrlKey);
14435     },
14436     "enter" : function(e){
14437         this.save();
14438     },
14439     scope : this
14440 });
14441 </code></pre>
14442  * @constructor
14443  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14444  * @param {Object} config The config
14445  */
14446 Roo.KeyNav = function(el, config){
14447     this.el = Roo.get(el);
14448     Roo.apply(this, config);
14449     if(!this.disabled){
14450         this.disabled = true;
14451         this.enable();
14452     }
14453 };
14454
14455 Roo.KeyNav.prototype = {
14456     /**
14457      * @cfg {Boolean} disabled
14458      * True to disable this KeyNav instance (defaults to false)
14459      */
14460     disabled : false,
14461     /**
14462      * @cfg {String} defaultEventAction
14463      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14464      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14465      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14466      */
14467     defaultEventAction: "stopEvent",
14468     /**
14469      * @cfg {Boolean} forceKeyDown
14470      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14471      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14472      * handle keydown instead of keypress.
14473      */
14474     forceKeyDown : false,
14475
14476     // private
14477     prepareEvent : function(e){
14478         var k = e.getKey();
14479         var h = this.keyToHandler[k];
14480         //if(h && this[h]){
14481         //    e.stopPropagation();
14482         //}
14483         if(Roo.isSafari && h && k >= 37 && k <= 40){
14484             e.stopEvent();
14485         }
14486     },
14487
14488     // private
14489     relay : function(e){
14490         var k = e.getKey();
14491         var h = this.keyToHandler[k];
14492         if(h && this[h]){
14493             if(this.doRelay(e, this[h], h) !== true){
14494                 e[this.defaultEventAction]();
14495             }
14496         }
14497     },
14498
14499     // private
14500     doRelay : function(e, h, hname){
14501         return h.call(this.scope || this, e);
14502     },
14503
14504     // possible handlers
14505     enter : false,
14506     left : false,
14507     right : false,
14508     up : false,
14509     down : false,
14510     tab : false,
14511     esc : false,
14512     pageUp : false,
14513     pageDown : false,
14514     del : false,
14515     home : false,
14516     end : false,
14517
14518     // quick lookup hash
14519     keyToHandler : {
14520         37 : "left",
14521         39 : "right",
14522         38 : "up",
14523         40 : "down",
14524         33 : "pageUp",
14525         34 : "pageDown",
14526         46 : "del",
14527         36 : "home",
14528         35 : "end",
14529         13 : "enter",
14530         27 : "esc",
14531         9  : "tab"
14532     },
14533
14534         /**
14535          * Enable this KeyNav
14536          */
14537         enable: function(){
14538                 if(this.disabled){
14539             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14540             // the EventObject will normalize Safari automatically
14541             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14542                 this.el.on("keydown", this.relay,  this);
14543             }else{
14544                 this.el.on("keydown", this.prepareEvent,  this);
14545                 this.el.on("keypress", this.relay,  this);
14546             }
14547                     this.disabled = false;
14548                 }
14549         },
14550
14551         /**
14552          * Disable this KeyNav
14553          */
14554         disable: function(){
14555                 if(!this.disabled){
14556                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14557                 this.el.un("keydown", this.relay);
14558             }else{
14559                 this.el.un("keydown", this.prepareEvent);
14560                 this.el.un("keypress", this.relay);
14561             }
14562                     this.disabled = true;
14563                 }
14564         }
14565 };/*
14566  * Based on:
14567  * Ext JS Library 1.1.1
14568  * Copyright(c) 2006-2007, Ext JS, LLC.
14569  *
14570  * Originally Released Under LGPL - original licence link has changed is not relivant.
14571  *
14572  * Fork - LGPL
14573  * <script type="text/javascript">
14574  */
14575
14576  
14577 /**
14578  * @class Roo.KeyMap
14579  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14580  * The constructor accepts the same config object as defined by {@link #addBinding}.
14581  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14582  * combination it will call the function with this signature (if the match is a multi-key
14583  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14584  * A KeyMap can also handle a string representation of keys.<br />
14585  * Usage:
14586  <pre><code>
14587 // map one key by key code
14588 var map = new Roo.KeyMap("my-element", {
14589     key: 13, // or Roo.EventObject.ENTER
14590     fn: myHandler,
14591     scope: myObject
14592 });
14593
14594 // map multiple keys to one action by string
14595 var map = new Roo.KeyMap("my-element", {
14596     key: "a\r\n\t",
14597     fn: myHandler,
14598     scope: myObject
14599 });
14600
14601 // map multiple keys to multiple actions by strings and array of codes
14602 var map = new Roo.KeyMap("my-element", [
14603     {
14604         key: [10,13],
14605         fn: function(){ alert("Return was pressed"); }
14606     }, {
14607         key: "abc",
14608         fn: function(){ alert('a, b or c was pressed'); }
14609     }, {
14610         key: "\t",
14611         ctrl:true,
14612         shift:true,
14613         fn: function(){ alert('Control + shift + tab was pressed.'); }
14614     }
14615 ]);
14616 </code></pre>
14617  * <b>Note: A KeyMap starts enabled</b>
14618  * @constructor
14619  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14620  * @param {Object} config The config (see {@link #addBinding})
14621  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14622  */
14623 Roo.KeyMap = function(el, config, eventName){
14624     this.el  = Roo.get(el);
14625     this.eventName = eventName || "keydown";
14626     this.bindings = [];
14627     if(config){
14628         this.addBinding(config);
14629     }
14630     this.enable();
14631 };
14632
14633 Roo.KeyMap.prototype = {
14634     /**
14635      * True to stop the event from bubbling and prevent the default browser action if the
14636      * key was handled by the KeyMap (defaults to false)
14637      * @type Boolean
14638      */
14639     stopEvent : false,
14640
14641     /**
14642      * Add a new binding to this KeyMap. The following config object properties are supported:
14643      * <pre>
14644 Property    Type             Description
14645 ----------  ---------------  ----------------------------------------------------------------------
14646 key         String/Array     A single keycode or an array of keycodes to handle
14647 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14648 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14649 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14650 fn          Function         The function to call when KeyMap finds the expected key combination
14651 scope       Object           The scope of the callback function
14652 </pre>
14653      *
14654      * Usage:
14655      * <pre><code>
14656 // Create a KeyMap
14657 var map = new Roo.KeyMap(document, {
14658     key: Roo.EventObject.ENTER,
14659     fn: handleKey,
14660     scope: this
14661 });
14662
14663 //Add a new binding to the existing KeyMap later
14664 map.addBinding({
14665     key: 'abc',
14666     shift: true,
14667     fn: handleKey,
14668     scope: this
14669 });
14670 </code></pre>
14671      * @param {Object/Array} config A single KeyMap config or an array of configs
14672      */
14673         addBinding : function(config){
14674         if(config instanceof Array){
14675             for(var i = 0, len = config.length; i < len; i++){
14676                 this.addBinding(config[i]);
14677             }
14678             return;
14679         }
14680         var keyCode = config.key,
14681             shift = config.shift, 
14682             ctrl = config.ctrl, 
14683             alt = config.alt,
14684             fn = config.fn,
14685             scope = config.scope;
14686         if(typeof keyCode == "string"){
14687             var ks = [];
14688             var keyString = keyCode.toUpperCase();
14689             for(var j = 0, len = keyString.length; j < len; j++){
14690                 ks.push(keyString.charCodeAt(j));
14691             }
14692             keyCode = ks;
14693         }
14694         var keyArray = keyCode instanceof Array;
14695         var handler = function(e){
14696             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14697                 var k = e.getKey();
14698                 if(keyArray){
14699                     for(var i = 0, len = keyCode.length; i < len; i++){
14700                         if(keyCode[i] == k){
14701                           if(this.stopEvent){
14702                               e.stopEvent();
14703                           }
14704                           fn.call(scope || window, k, e);
14705                           return;
14706                         }
14707                     }
14708                 }else{
14709                     if(k == keyCode){
14710                         if(this.stopEvent){
14711                            e.stopEvent();
14712                         }
14713                         fn.call(scope || window, k, e);
14714                     }
14715                 }
14716             }
14717         };
14718         this.bindings.push(handler);  
14719         },
14720
14721     /**
14722      * Shorthand for adding a single key listener
14723      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14724      * following options:
14725      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14726      * @param {Function} fn The function to call
14727      * @param {Object} scope (optional) The scope of the function
14728      */
14729     on : function(key, fn, scope){
14730         var keyCode, shift, ctrl, alt;
14731         if(typeof key == "object" && !(key instanceof Array)){
14732             keyCode = key.key;
14733             shift = key.shift;
14734             ctrl = key.ctrl;
14735             alt = key.alt;
14736         }else{
14737             keyCode = key;
14738         }
14739         this.addBinding({
14740             key: keyCode,
14741             shift: shift,
14742             ctrl: ctrl,
14743             alt: alt,
14744             fn: fn,
14745             scope: scope
14746         })
14747     },
14748
14749     // private
14750     handleKeyDown : function(e){
14751             if(this.enabled){ //just in case
14752             var b = this.bindings;
14753             for(var i = 0, len = b.length; i < len; i++){
14754                 b[i].call(this, e);
14755             }
14756             }
14757         },
14758         
14759         /**
14760          * Returns true if this KeyMap is enabled
14761          * @return {Boolean} 
14762          */
14763         isEnabled : function(){
14764             return this.enabled;  
14765         },
14766         
14767         /**
14768          * Enables this KeyMap
14769          */
14770         enable: function(){
14771                 if(!this.enabled){
14772                     this.el.on(this.eventName, this.handleKeyDown, this);
14773                     this.enabled = true;
14774                 }
14775         },
14776
14777         /**
14778          * Disable this KeyMap
14779          */
14780         disable: function(){
14781                 if(this.enabled){
14782                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14783                     this.enabled = false;
14784                 }
14785         }
14786 };/*
14787  * Based on:
14788  * Ext JS Library 1.1.1
14789  * Copyright(c) 2006-2007, Ext JS, LLC.
14790  *
14791  * Originally Released Under LGPL - original licence link has changed is not relivant.
14792  *
14793  * Fork - LGPL
14794  * <script type="text/javascript">
14795  */
14796
14797  
14798 /**
14799  * @class Roo.util.TextMetrics
14800  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14801  * wide, in pixels, a given block of text will be.
14802  * @singleton
14803  */
14804 Roo.util.TextMetrics = function(){
14805     var shared;
14806     return {
14807         /**
14808          * Measures the size of the specified text
14809          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14810          * that can affect the size of the rendered text
14811          * @param {String} text The text to measure
14812          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14813          * in order to accurately measure the text height
14814          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14815          */
14816         measure : function(el, text, fixedWidth){
14817             if(!shared){
14818                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14819             }
14820             shared.bind(el);
14821             shared.setFixedWidth(fixedWidth || 'auto');
14822             return shared.getSize(text);
14823         },
14824
14825         /**
14826          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14827          * the overhead of multiple calls to initialize the style properties on each measurement.
14828          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14829          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14830          * in order to accurately measure the text height
14831          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14832          */
14833         createInstance : function(el, fixedWidth){
14834             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14835         }
14836     };
14837 }();
14838
14839  
14840
14841 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14842     var ml = new Roo.Element(document.createElement('div'));
14843     document.body.appendChild(ml.dom);
14844     ml.position('absolute');
14845     ml.setLeftTop(-1000, -1000);
14846     ml.hide();
14847
14848     if(fixedWidth){
14849         ml.setWidth(fixedWidth);
14850     }
14851      
14852     var instance = {
14853         /**
14854          * Returns the size of the specified text based on the internal element's style and width properties
14855          * @memberOf Roo.util.TextMetrics.Instance#
14856          * @param {String} text The text to measure
14857          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14858          */
14859         getSize : function(text){
14860             ml.update(text);
14861             var s = ml.getSize();
14862             ml.update('');
14863             return s;
14864         },
14865
14866         /**
14867          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14868          * that can affect the size of the rendered text
14869          * @memberOf Roo.util.TextMetrics.Instance#
14870          * @param {String/HTMLElement} el The element, dom node or id
14871          */
14872         bind : function(el){
14873             ml.setStyle(
14874                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14875             );
14876         },
14877
14878         /**
14879          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14880          * to set a fixed width in order to accurately measure the text height.
14881          * @memberOf Roo.util.TextMetrics.Instance#
14882          * @param {Number} width The width to set on the element
14883          */
14884         setFixedWidth : function(width){
14885             ml.setWidth(width);
14886         },
14887
14888         /**
14889          * Returns the measured width of the specified text
14890          * @memberOf Roo.util.TextMetrics.Instance#
14891          * @param {String} text The text to measure
14892          * @return {Number} width The width in pixels
14893          */
14894         getWidth : function(text){
14895             ml.dom.style.width = 'auto';
14896             return this.getSize(text).width;
14897         },
14898
14899         /**
14900          * Returns the measured height of the specified text.  For multiline text, be sure to call
14901          * {@link #setFixedWidth} if necessary.
14902          * @memberOf Roo.util.TextMetrics.Instance#
14903          * @param {String} text The text to measure
14904          * @return {Number} height The height in pixels
14905          */
14906         getHeight : function(text){
14907             return this.getSize(text).height;
14908         }
14909     };
14910
14911     instance.bind(bindTo);
14912
14913     return instance;
14914 };
14915
14916 // backwards compat
14917 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14918  * Based on:
14919  * Ext JS Library 1.1.1
14920  * Copyright(c) 2006-2007, Ext JS, LLC.
14921  *
14922  * Originally Released Under LGPL - original licence link has changed is not relivant.
14923  *
14924  * Fork - LGPL
14925  * <script type="text/javascript">
14926  */
14927
14928 /**
14929  * @class Roo.state.Provider
14930  * Abstract base class for state provider implementations. This class provides methods
14931  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14932  * Provider interface.
14933  */
14934 Roo.state.Provider = function(){
14935     /**
14936      * @event statechange
14937      * Fires when a state change occurs.
14938      * @param {Provider} this This state provider
14939      * @param {String} key The state key which was changed
14940      * @param {String} value The encoded value for the state
14941      */
14942     this.addEvents({
14943         "statechange": true
14944     });
14945     this.state = {};
14946     Roo.state.Provider.superclass.constructor.call(this);
14947 };
14948 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14949     /**
14950      * Returns the current value for a key
14951      * @param {String} name The key name
14952      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14953      * @return {Mixed} The state data
14954      */
14955     get : function(name, defaultValue){
14956         return typeof this.state[name] == "undefined" ?
14957             defaultValue : this.state[name];
14958     },
14959     
14960     /**
14961      * Clears a value from the state
14962      * @param {String} name The key name
14963      */
14964     clear : function(name){
14965         delete this.state[name];
14966         this.fireEvent("statechange", this, name, null);
14967     },
14968     
14969     /**
14970      * Sets the value for a key
14971      * @param {String} name The key name
14972      * @param {Mixed} value The value to set
14973      */
14974     set : function(name, value){
14975         this.state[name] = value;
14976         this.fireEvent("statechange", this, name, value);
14977     },
14978     
14979     /**
14980      * Decodes a string previously encoded with {@link #encodeValue}.
14981      * @param {String} value The value to decode
14982      * @return {Mixed} The decoded value
14983      */
14984     decodeValue : function(cookie){
14985         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14986         var matches = re.exec(unescape(cookie));
14987         if(!matches || !matches[1]) {
14988             return; // non state cookie
14989         }
14990         var type = matches[1];
14991         var v = matches[2];
14992         switch(type){
14993             case "n":
14994                 return parseFloat(v);
14995             case "d":
14996                 return new Date(Date.parse(v));
14997             case "b":
14998                 return (v == "1");
14999             case "a":
15000                 var all = [];
15001                 var values = v.split("^");
15002                 for(var i = 0, len = values.length; i < len; i++){
15003                     all.push(this.decodeValue(values[i]));
15004                 }
15005                 return all;
15006            case "o":
15007                 var all = {};
15008                 var values = v.split("^");
15009                 for(var i = 0, len = values.length; i < len; i++){
15010                     var kv = values[i].split("=");
15011                     all[kv[0]] = this.decodeValue(kv[1]);
15012                 }
15013                 return all;
15014            default:
15015                 return v;
15016         }
15017     },
15018     
15019     /**
15020      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15021      * @param {Mixed} value The value to encode
15022      * @return {String} The encoded value
15023      */
15024     encodeValue : function(v){
15025         var enc;
15026         if(typeof v == "number"){
15027             enc = "n:" + v;
15028         }else if(typeof v == "boolean"){
15029             enc = "b:" + (v ? "1" : "0");
15030         }else if(v instanceof Date){
15031             enc = "d:" + v.toGMTString();
15032         }else if(v instanceof Array){
15033             var flat = "";
15034             for(var i = 0, len = v.length; i < len; i++){
15035                 flat += this.encodeValue(v[i]);
15036                 if(i != len-1) {
15037                     flat += "^";
15038                 }
15039             }
15040             enc = "a:" + flat;
15041         }else if(typeof v == "object"){
15042             var flat = "";
15043             for(var key in v){
15044                 if(typeof v[key] != "function"){
15045                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15046                 }
15047             }
15048             enc = "o:" + flat.substring(0, flat.length-1);
15049         }else{
15050             enc = "s:" + v;
15051         }
15052         return escape(enc);        
15053     }
15054 });
15055
15056 /*
15057  * Based on:
15058  * Ext JS Library 1.1.1
15059  * Copyright(c) 2006-2007, Ext JS, LLC.
15060  *
15061  * Originally Released Under LGPL - original licence link has changed is not relivant.
15062  *
15063  * Fork - LGPL
15064  * <script type="text/javascript">
15065  */
15066 /**
15067  * @class Roo.state.Manager
15068  * This is the global state manager. By default all components that are "state aware" check this class
15069  * for state information if you don't pass them a custom state provider. In order for this class
15070  * to be useful, it must be initialized with a provider when your application initializes.
15071  <pre><code>
15072 // in your initialization function
15073 init : function(){
15074    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15075    ...
15076    // supposed you have a {@link Roo.BorderLayout}
15077    var layout = new Roo.BorderLayout(...);
15078    layout.restoreState();
15079    // or a {Roo.BasicDialog}
15080    var dialog = new Roo.BasicDialog(...);
15081    dialog.restoreState();
15082  </code></pre>
15083  * @singleton
15084  */
15085 Roo.state.Manager = function(){
15086     var provider = new Roo.state.Provider();
15087     
15088     return {
15089         /**
15090          * Configures the default state provider for your application
15091          * @param {Provider} stateProvider The state provider to set
15092          */
15093         setProvider : function(stateProvider){
15094             provider = stateProvider;
15095         },
15096         
15097         /**
15098          * Returns the current value for a key
15099          * @param {String} name The key name
15100          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15101          * @return {Mixed} The state data
15102          */
15103         get : function(key, defaultValue){
15104             return provider.get(key, defaultValue);
15105         },
15106         
15107         /**
15108          * Sets the value for a key
15109          * @param {String} name The key name
15110          * @param {Mixed} value The state data
15111          */
15112          set : function(key, value){
15113             provider.set(key, value);
15114         },
15115         
15116         /**
15117          * Clears a value from the state
15118          * @param {String} name The key name
15119          */
15120         clear : function(key){
15121             provider.clear(key);
15122         },
15123         
15124         /**
15125          * Gets the currently configured state provider
15126          * @return {Provider} The state provider
15127          */
15128         getProvider : function(){
15129             return provider;
15130         }
15131     };
15132 }();
15133 /*
15134  * Based on:
15135  * Ext JS Library 1.1.1
15136  * Copyright(c) 2006-2007, Ext JS, LLC.
15137  *
15138  * Originally Released Under LGPL - original licence link has changed is not relivant.
15139  *
15140  * Fork - LGPL
15141  * <script type="text/javascript">
15142  */
15143 /**
15144  * @class Roo.state.CookieProvider
15145  * @extends Roo.state.Provider
15146  * The default Provider implementation which saves state via cookies.
15147  * <br />Usage:
15148  <pre><code>
15149    var cp = new Roo.state.CookieProvider({
15150        path: "/cgi-bin/",
15151        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15152        domain: "roojs.com"
15153    })
15154    Roo.state.Manager.setProvider(cp);
15155  </code></pre>
15156  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15157  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15158  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15159  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15160  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15161  * domain the page is running on including the 'www' like 'www.roojs.com')
15162  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15163  * @constructor
15164  * Create a new CookieProvider
15165  * @param {Object} config The configuration object
15166  */
15167 Roo.state.CookieProvider = function(config){
15168     Roo.state.CookieProvider.superclass.constructor.call(this);
15169     this.path = "/";
15170     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15171     this.domain = null;
15172     this.secure = false;
15173     Roo.apply(this, config);
15174     this.state = this.readCookies();
15175 };
15176
15177 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15178     // private
15179     set : function(name, value){
15180         if(typeof value == "undefined" || value === null){
15181             this.clear(name);
15182             return;
15183         }
15184         this.setCookie(name, value);
15185         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15186     },
15187
15188     // private
15189     clear : function(name){
15190         this.clearCookie(name);
15191         Roo.state.CookieProvider.superclass.clear.call(this, name);
15192     },
15193
15194     // private
15195     readCookies : function(){
15196         var cookies = {};
15197         var c = document.cookie + ";";
15198         var re = /\s?(.*?)=(.*?);/g;
15199         var matches;
15200         while((matches = re.exec(c)) != null){
15201             var name = matches[1];
15202             var value = matches[2];
15203             if(name && name.substring(0,3) == "ys-"){
15204                 cookies[name.substr(3)] = this.decodeValue(value);
15205             }
15206         }
15207         return cookies;
15208     },
15209
15210     // private
15211     setCookie : function(name, value){
15212         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15213            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15214            ((this.path == null) ? "" : ("; path=" + this.path)) +
15215            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15216            ((this.secure == true) ? "; secure" : "");
15217     },
15218
15219     // private
15220     clearCookie : function(name){
15221         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15222            ((this.path == null) ? "" : ("; path=" + this.path)) +
15223            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15224            ((this.secure == true) ? "; secure" : "");
15225     }
15226 });/*
15227  * Based on:
15228  * Ext JS Library 1.1.1
15229  * Copyright(c) 2006-2007, Ext JS, LLC.
15230  *
15231  * Originally Released Under LGPL - original licence link has changed is not relivant.
15232  *
15233  * Fork - LGPL
15234  * <script type="text/javascript">
15235  */
15236  
15237
15238 /**
15239  * @class Roo.ComponentMgr
15240  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15241  * @singleton
15242  */
15243 Roo.ComponentMgr = function(){
15244     var all = new Roo.util.MixedCollection();
15245
15246     return {
15247         /**
15248          * Registers a component.
15249          * @param {Roo.Component} c The component
15250          */
15251         register : function(c){
15252             all.add(c);
15253         },
15254
15255         /**
15256          * Unregisters a component.
15257          * @param {Roo.Component} c The component
15258          */
15259         unregister : function(c){
15260             all.remove(c);
15261         },
15262
15263         /**
15264          * Returns a component by id
15265          * @param {String} id The component id
15266          */
15267         get : function(id){
15268             return all.get(id);
15269         },
15270
15271         /**
15272          * Registers a function that will be called when a specified component is added to ComponentMgr
15273          * @param {String} id The component id
15274          * @param {Funtction} fn The callback function
15275          * @param {Object} scope The scope of the callback
15276          */
15277         onAvailable : function(id, fn, scope){
15278             all.on("add", function(index, o){
15279                 if(o.id == id){
15280                     fn.call(scope || o, o);
15281                     all.un("add", fn, scope);
15282                 }
15283             });
15284         }
15285     };
15286 }();/*
15287  * Based on:
15288  * Ext JS Library 1.1.1
15289  * Copyright(c) 2006-2007, Ext JS, LLC.
15290  *
15291  * Originally Released Under LGPL - original licence link has changed is not relivant.
15292  *
15293  * Fork - LGPL
15294  * <script type="text/javascript">
15295  */
15296  
15297 /**
15298  * @class Roo.Component
15299  * @extends Roo.util.Observable
15300  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15301  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15302  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15303  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15304  * All visual components (widgets) that require rendering into a layout should subclass Component.
15305  * @constructor
15306  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15307  * 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
15308  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15309  */
15310 Roo.Component = function(config){
15311     config = config || {};
15312     if(config.tagName || config.dom || typeof config == "string"){ // element object
15313         config = {el: config, id: config.id || config};
15314     }
15315     this.initialConfig = config;
15316
15317     Roo.apply(this, config);
15318     this.addEvents({
15319         /**
15320          * @event disable
15321          * Fires after the component is disabled.
15322              * @param {Roo.Component} this
15323              */
15324         disable : true,
15325         /**
15326          * @event enable
15327          * Fires after the component is enabled.
15328              * @param {Roo.Component} this
15329              */
15330         enable : true,
15331         /**
15332          * @event beforeshow
15333          * Fires before the component is shown.  Return false to stop the show.
15334              * @param {Roo.Component} this
15335              */
15336         beforeshow : true,
15337         /**
15338          * @event show
15339          * Fires after the component is shown.
15340              * @param {Roo.Component} this
15341              */
15342         show : true,
15343         /**
15344          * @event beforehide
15345          * Fires before the component is hidden. Return false to stop the hide.
15346              * @param {Roo.Component} this
15347              */
15348         beforehide : true,
15349         /**
15350          * @event hide
15351          * Fires after the component is hidden.
15352              * @param {Roo.Component} this
15353              */
15354         hide : true,
15355         /**
15356          * @event beforerender
15357          * Fires before the component is rendered. Return false to stop the render.
15358              * @param {Roo.Component} this
15359              */
15360         beforerender : true,
15361         /**
15362          * @event render
15363          * Fires after the component is rendered.
15364              * @param {Roo.Component} this
15365              */
15366         render : true,
15367         /**
15368          * @event beforedestroy
15369          * Fires before the component is destroyed. Return false to stop the destroy.
15370              * @param {Roo.Component} this
15371              */
15372         beforedestroy : true,
15373         /**
15374          * @event destroy
15375          * Fires after the component is destroyed.
15376              * @param {Roo.Component} this
15377              */
15378         destroy : true
15379     });
15380     if(!this.id){
15381         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15382     }
15383     Roo.ComponentMgr.register(this);
15384     Roo.Component.superclass.constructor.call(this);
15385     this.initComponent();
15386     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15387         this.render(this.renderTo);
15388         delete this.renderTo;
15389     }
15390 };
15391
15392 /** @private */
15393 Roo.Component.AUTO_ID = 1000;
15394
15395 Roo.extend(Roo.Component, Roo.util.Observable, {
15396     /**
15397      * @scope Roo.Component.prototype
15398      * @type {Boolean}
15399      * true if this component is hidden. Read-only.
15400      */
15401     hidden : false,
15402     /**
15403      * @type {Boolean}
15404      * true if this component is disabled. Read-only.
15405      */
15406     disabled : false,
15407     /**
15408      * @type {Boolean}
15409      * true if this component has been rendered. Read-only.
15410      */
15411     rendered : false,
15412     
15413     /** @cfg {String} disableClass
15414      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15415      */
15416     disabledClass : "x-item-disabled",
15417         /** @cfg {Boolean} allowDomMove
15418          * Whether the component can move the Dom node when rendering (defaults to true).
15419          */
15420     allowDomMove : true,
15421     /** @cfg {String} hideMode (display|visibility)
15422      * How this component should hidden. Supported values are
15423      * "visibility" (css visibility), "offsets" (negative offset position) and
15424      * "display" (css display) - defaults to "display".
15425      */
15426     hideMode: 'display',
15427
15428     /** @private */
15429     ctype : "Roo.Component",
15430
15431     /**
15432      * @cfg {String} actionMode 
15433      * which property holds the element that used for  hide() / show() / disable() / enable()
15434      * default is 'el' 
15435      */
15436     actionMode : "el",
15437
15438     /** @private */
15439     getActionEl : function(){
15440         return this[this.actionMode];
15441     },
15442
15443     initComponent : Roo.emptyFn,
15444     /**
15445      * If this is a lazy rendering component, render it to its container element.
15446      * @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.
15447      */
15448     render : function(container, position){
15449         
15450         if(this.rendered){
15451             return this;
15452         }
15453         
15454         if(this.fireEvent("beforerender", this) === false){
15455             return false;
15456         }
15457         
15458         if(!container && this.el){
15459             this.el = Roo.get(this.el);
15460             container = this.el.dom.parentNode;
15461             this.allowDomMove = false;
15462         }
15463         this.container = Roo.get(container);
15464         this.rendered = true;
15465         if(position !== undefined){
15466             if(typeof position == 'number'){
15467                 position = this.container.dom.childNodes[position];
15468             }else{
15469                 position = Roo.getDom(position);
15470             }
15471         }
15472         this.onRender(this.container, position || null);
15473         if(this.cls){
15474             this.el.addClass(this.cls);
15475             delete this.cls;
15476         }
15477         if(this.style){
15478             this.el.applyStyles(this.style);
15479             delete this.style;
15480         }
15481         this.fireEvent("render", this);
15482         this.afterRender(this.container);
15483         if(this.hidden){
15484             this.hide();
15485         }
15486         if(this.disabled){
15487             this.disable();
15488         }
15489
15490         return this;
15491         
15492     },
15493
15494     /** @private */
15495     // default function is not really useful
15496     onRender : function(ct, position){
15497         if(this.el){
15498             this.el = Roo.get(this.el);
15499             if(this.allowDomMove !== false){
15500                 ct.dom.insertBefore(this.el.dom, position);
15501             }
15502         }
15503     },
15504
15505     /** @private */
15506     getAutoCreate : function(){
15507         var cfg = typeof this.autoCreate == "object" ?
15508                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15509         if(this.id && !cfg.id){
15510             cfg.id = this.id;
15511         }
15512         return cfg;
15513     },
15514
15515     /** @private */
15516     afterRender : Roo.emptyFn,
15517
15518     /**
15519      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15520      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15521      */
15522     destroy : function(){
15523         if(this.fireEvent("beforedestroy", this) !== false){
15524             this.purgeListeners();
15525             this.beforeDestroy();
15526             if(this.rendered){
15527                 this.el.removeAllListeners();
15528                 this.el.remove();
15529                 if(this.actionMode == "container"){
15530                     this.container.remove();
15531                 }
15532             }
15533             this.onDestroy();
15534             Roo.ComponentMgr.unregister(this);
15535             this.fireEvent("destroy", this);
15536         }
15537     },
15538
15539         /** @private */
15540     beforeDestroy : function(){
15541
15542     },
15543
15544         /** @private */
15545         onDestroy : function(){
15546
15547     },
15548
15549     /**
15550      * Returns the underlying {@link Roo.Element}.
15551      * @return {Roo.Element} The element
15552      */
15553     getEl : function(){
15554         return this.el;
15555     },
15556
15557     /**
15558      * Returns the id of this component.
15559      * @return {String}
15560      */
15561     getId : function(){
15562         return this.id;
15563     },
15564
15565     /**
15566      * Try to focus this component.
15567      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15568      * @return {Roo.Component} this
15569      */
15570     focus : function(selectText){
15571         if(this.rendered){
15572             this.el.focus();
15573             if(selectText === true){
15574                 this.el.dom.select();
15575             }
15576         }
15577         return this;
15578     },
15579
15580     /** @private */
15581     blur : function(){
15582         if(this.rendered){
15583             this.el.blur();
15584         }
15585         return this;
15586     },
15587
15588     /**
15589      * Disable this component.
15590      * @return {Roo.Component} this
15591      */
15592     disable : function(){
15593         if(this.rendered){
15594             this.onDisable();
15595         }
15596         this.disabled = true;
15597         this.fireEvent("disable", this);
15598         return this;
15599     },
15600
15601         // private
15602     onDisable : function(){
15603         this.getActionEl().addClass(this.disabledClass);
15604         this.el.dom.disabled = true;
15605     },
15606
15607     /**
15608      * Enable this component.
15609      * @return {Roo.Component} this
15610      */
15611     enable : function(){
15612         if(this.rendered){
15613             this.onEnable();
15614         }
15615         this.disabled = false;
15616         this.fireEvent("enable", this);
15617         return this;
15618     },
15619
15620         // private
15621     onEnable : function(){
15622         this.getActionEl().removeClass(this.disabledClass);
15623         this.el.dom.disabled = false;
15624     },
15625
15626     /**
15627      * Convenience function for setting disabled/enabled by boolean.
15628      * @param {Boolean} disabled
15629      */
15630     setDisabled : function(disabled){
15631         this[disabled ? "disable" : "enable"]();
15632     },
15633
15634     /**
15635      * Show this component.
15636      * @return {Roo.Component} this
15637      */
15638     show: function(){
15639         if(this.fireEvent("beforeshow", this) !== false){
15640             this.hidden = false;
15641             if(this.rendered){
15642                 this.onShow();
15643             }
15644             this.fireEvent("show", this);
15645         }
15646         return this;
15647     },
15648
15649     // private
15650     onShow : function(){
15651         var ae = this.getActionEl();
15652         if(this.hideMode == 'visibility'){
15653             ae.dom.style.visibility = "visible";
15654         }else if(this.hideMode == 'offsets'){
15655             ae.removeClass('x-hidden');
15656         }else{
15657             ae.dom.style.display = "";
15658         }
15659     },
15660
15661     /**
15662      * Hide this component.
15663      * @return {Roo.Component} this
15664      */
15665     hide: function(){
15666         if(this.fireEvent("beforehide", this) !== false){
15667             this.hidden = true;
15668             if(this.rendered){
15669                 this.onHide();
15670             }
15671             this.fireEvent("hide", this);
15672         }
15673         return this;
15674     },
15675
15676     // private
15677     onHide : function(){
15678         var ae = this.getActionEl();
15679         if(this.hideMode == 'visibility'){
15680             ae.dom.style.visibility = "hidden";
15681         }else if(this.hideMode == 'offsets'){
15682             ae.addClass('x-hidden');
15683         }else{
15684             ae.dom.style.display = "none";
15685         }
15686     },
15687
15688     /**
15689      * Convenience function to hide or show this component by boolean.
15690      * @param {Boolean} visible True to show, false to hide
15691      * @return {Roo.Component} this
15692      */
15693     setVisible: function(visible){
15694         if(visible) {
15695             this.show();
15696         }else{
15697             this.hide();
15698         }
15699         return this;
15700     },
15701
15702     /**
15703      * Returns true if this component is visible.
15704      */
15705     isVisible : function(){
15706         return this.getActionEl().isVisible();
15707     },
15708
15709     cloneConfig : function(overrides){
15710         overrides = overrides || {};
15711         var id = overrides.id || Roo.id();
15712         var cfg = Roo.applyIf(overrides, this.initialConfig);
15713         cfg.id = id; // prevent dup id
15714         return new this.constructor(cfg);
15715     }
15716 });/*
15717  * Based on:
15718  * Ext JS Library 1.1.1
15719  * Copyright(c) 2006-2007, Ext JS, LLC.
15720  *
15721  * Originally Released Under LGPL - original licence link has changed is not relivant.
15722  *
15723  * Fork - LGPL
15724  * <script type="text/javascript">
15725  */
15726
15727 /**
15728  * @class Roo.BoxComponent
15729  * @extends Roo.Component
15730  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15731  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15732  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15733  * layout containers.
15734  * @constructor
15735  * @param {Roo.Element/String/Object} config The configuration options.
15736  */
15737 Roo.BoxComponent = function(config){
15738     Roo.Component.call(this, config);
15739     this.addEvents({
15740         /**
15741          * @event resize
15742          * Fires after the component is resized.
15743              * @param {Roo.Component} this
15744              * @param {Number} adjWidth The box-adjusted width that was set
15745              * @param {Number} adjHeight The box-adjusted height that was set
15746              * @param {Number} rawWidth The width that was originally specified
15747              * @param {Number} rawHeight The height that was originally specified
15748              */
15749         resize : true,
15750         /**
15751          * @event move
15752          * Fires after the component is moved.
15753              * @param {Roo.Component} this
15754              * @param {Number} x The new x position
15755              * @param {Number} y The new y position
15756              */
15757         move : true
15758     });
15759 };
15760
15761 Roo.extend(Roo.BoxComponent, Roo.Component, {
15762     // private, set in afterRender to signify that the component has been rendered
15763     boxReady : false,
15764     // private, used to defer height settings to subclasses
15765     deferHeight: false,
15766     /** @cfg {Number} width
15767      * width (optional) size of component
15768      */
15769      /** @cfg {Number} height
15770      * height (optional) size of component
15771      */
15772      
15773     /**
15774      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15775      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15776      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15777      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15778      * @return {Roo.BoxComponent} this
15779      */
15780     setSize : function(w, h){
15781         // support for standard size objects
15782         if(typeof w == 'object'){
15783             h = w.height;
15784             w = w.width;
15785         }
15786         // not rendered
15787         if(!this.boxReady){
15788             this.width = w;
15789             this.height = h;
15790             return this;
15791         }
15792
15793         // prevent recalcs when not needed
15794         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15795             return this;
15796         }
15797         this.lastSize = {width: w, height: h};
15798
15799         var adj = this.adjustSize(w, h);
15800         var aw = adj.width, ah = adj.height;
15801         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15802             var rz = this.getResizeEl();
15803             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15804                 rz.setSize(aw, ah);
15805             }else if(!this.deferHeight && ah !== undefined){
15806                 rz.setHeight(ah);
15807             }else if(aw !== undefined){
15808                 rz.setWidth(aw);
15809             }
15810             this.onResize(aw, ah, w, h);
15811             this.fireEvent('resize', this, aw, ah, w, h);
15812         }
15813         return this;
15814     },
15815
15816     /**
15817      * Gets the current size of the component's underlying element.
15818      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15819      */
15820     getSize : function(){
15821         return this.el.getSize();
15822     },
15823
15824     /**
15825      * Gets the current XY position of the component's underlying element.
15826      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15827      * @return {Array} The XY position of the element (e.g., [100, 200])
15828      */
15829     getPosition : function(local){
15830         if(local === true){
15831             return [this.el.getLeft(true), this.el.getTop(true)];
15832         }
15833         return this.xy || this.el.getXY();
15834     },
15835
15836     /**
15837      * Gets the current box measurements of the component's underlying element.
15838      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15839      * @returns {Object} box An object in the format {x, y, width, height}
15840      */
15841     getBox : function(local){
15842         var s = this.el.getSize();
15843         if(local){
15844             s.x = this.el.getLeft(true);
15845             s.y = this.el.getTop(true);
15846         }else{
15847             var xy = this.xy || this.el.getXY();
15848             s.x = xy[0];
15849             s.y = xy[1];
15850         }
15851         return s;
15852     },
15853
15854     /**
15855      * Sets the current box measurements of the component's underlying element.
15856      * @param {Object} box An object in the format {x, y, width, height}
15857      * @returns {Roo.BoxComponent} this
15858      */
15859     updateBox : function(box){
15860         this.setSize(box.width, box.height);
15861         this.setPagePosition(box.x, box.y);
15862         return this;
15863     },
15864
15865     // protected
15866     getResizeEl : function(){
15867         return this.resizeEl || this.el;
15868     },
15869
15870     // protected
15871     getPositionEl : function(){
15872         return this.positionEl || this.el;
15873     },
15874
15875     /**
15876      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15877      * This method fires the move event.
15878      * @param {Number} left The new left
15879      * @param {Number} top The new top
15880      * @returns {Roo.BoxComponent} this
15881      */
15882     setPosition : function(x, y){
15883         this.x = x;
15884         this.y = y;
15885         if(!this.boxReady){
15886             return this;
15887         }
15888         var adj = this.adjustPosition(x, y);
15889         var ax = adj.x, ay = adj.y;
15890
15891         var el = this.getPositionEl();
15892         if(ax !== undefined || ay !== undefined){
15893             if(ax !== undefined && ay !== undefined){
15894                 el.setLeftTop(ax, ay);
15895             }else if(ax !== undefined){
15896                 el.setLeft(ax);
15897             }else if(ay !== undefined){
15898                 el.setTop(ay);
15899             }
15900             this.onPosition(ax, ay);
15901             this.fireEvent('move', this, ax, ay);
15902         }
15903         return this;
15904     },
15905
15906     /**
15907      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15908      * This method fires the move event.
15909      * @param {Number} x The new x position
15910      * @param {Number} y The new y position
15911      * @returns {Roo.BoxComponent} this
15912      */
15913     setPagePosition : function(x, y){
15914         this.pageX = x;
15915         this.pageY = y;
15916         if(!this.boxReady){
15917             return;
15918         }
15919         if(x === undefined || y === undefined){ // cannot translate undefined points
15920             return;
15921         }
15922         var p = this.el.translatePoints(x, y);
15923         this.setPosition(p.left, p.top);
15924         return this;
15925     },
15926
15927     // private
15928     onRender : function(ct, position){
15929         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15930         if(this.resizeEl){
15931             this.resizeEl = Roo.get(this.resizeEl);
15932         }
15933         if(this.positionEl){
15934             this.positionEl = Roo.get(this.positionEl);
15935         }
15936     },
15937
15938     // private
15939     afterRender : function(){
15940         Roo.BoxComponent.superclass.afterRender.call(this);
15941         this.boxReady = true;
15942         this.setSize(this.width, this.height);
15943         if(this.x || this.y){
15944             this.setPosition(this.x, this.y);
15945         }
15946         if(this.pageX || this.pageY){
15947             this.setPagePosition(this.pageX, this.pageY);
15948         }
15949     },
15950
15951     /**
15952      * Force the component's size to recalculate based on the underlying element's current height and width.
15953      * @returns {Roo.BoxComponent} this
15954      */
15955     syncSize : function(){
15956         delete this.lastSize;
15957         this.setSize(this.el.getWidth(), this.el.getHeight());
15958         return this;
15959     },
15960
15961     /**
15962      * Called after the component is resized, this method is empty by default but can be implemented by any
15963      * subclass that needs to perform custom logic after a resize occurs.
15964      * @param {Number} adjWidth The box-adjusted width that was set
15965      * @param {Number} adjHeight The box-adjusted height that was set
15966      * @param {Number} rawWidth The width that was originally specified
15967      * @param {Number} rawHeight The height that was originally specified
15968      */
15969     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15970
15971     },
15972
15973     /**
15974      * Called after the component is moved, this method is empty by default but can be implemented by any
15975      * subclass that needs to perform custom logic after a move occurs.
15976      * @param {Number} x The new x position
15977      * @param {Number} y The new y position
15978      */
15979     onPosition : function(x, y){
15980
15981     },
15982
15983     // private
15984     adjustSize : function(w, h){
15985         if(this.autoWidth){
15986             w = 'auto';
15987         }
15988         if(this.autoHeight){
15989             h = 'auto';
15990         }
15991         return {width : w, height: h};
15992     },
15993
15994     // private
15995     adjustPosition : function(x, y){
15996         return {x : x, y: y};
15997     }
15998 });/*
15999  * Original code for Roojs - LGPL
16000  * <script type="text/javascript">
16001  */
16002  
16003 /**
16004  * @class Roo.XComponent
16005  * A delayed Element creator...
16006  * Or a way to group chunks of interface together.
16007  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16008  *  used in conjunction with XComponent.build() it will create an instance of each element,
16009  *  then call addxtype() to build the User interface.
16010  * 
16011  * Mypart.xyx = new Roo.XComponent({
16012
16013     parent : 'Mypart.xyz', // empty == document.element.!!
16014     order : '001',
16015     name : 'xxxx'
16016     region : 'xxxx'
16017     disabled : function() {} 
16018      
16019     tree : function() { // return an tree of xtype declared components
16020         var MODULE = this;
16021         return 
16022         {
16023             xtype : 'NestedLayoutPanel',
16024             // technicall
16025         }
16026      ]
16027  *})
16028  *
16029  *
16030  * It can be used to build a big heiracy, with parent etc.
16031  * or you can just use this to render a single compoent to a dom element
16032  * MYPART.render(Roo.Element | String(id) | dom_element )
16033  *
16034  *
16035  * Usage patterns.
16036  *
16037  * Classic Roo
16038  *
16039  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16040  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16041  *
16042  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16043  *
16044  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16045  * - if mulitple topModules exist, the last one is defined as the top module.
16046  *
16047  * Embeded Roo
16048  * 
16049  * When the top level or multiple modules are to embedded into a existing HTML page,
16050  * the parent element can container '#id' of the element where the module will be drawn.
16051  *
16052  * Bootstrap Roo
16053  *
16054  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16055  * it relies more on a include mechanism, where sub modules are included into an outer page.
16056  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16057  * 
16058  * Bootstrap Roo Included elements
16059  *
16060  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16061  * hence confusing the component builder as it thinks there are multiple top level elements. 
16062  *
16063  * String Over-ride & Translations
16064  *
16065  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16066  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16067  * are needed. @see Roo.XComponent.overlayString  
16068  * 
16069  * 
16070  * 
16071  * @extends Roo.util.Observable
16072  * @constructor
16073  * @param cfg {Object} configuration of component
16074  * 
16075  */
16076 Roo.XComponent = function(cfg) {
16077     Roo.apply(this, cfg);
16078     this.addEvents({ 
16079         /**
16080              * @event built
16081              * Fires when this the componnt is built
16082              * @param {Roo.XComponent} c the component
16083              */
16084         'built' : true
16085         
16086     });
16087     this.region = this.region || 'center'; // default..
16088     Roo.XComponent.register(this);
16089     this.modules = false;
16090     this.el = false; // where the layout goes..
16091     
16092     
16093 }
16094 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16095     /**
16096      * @property el
16097      * The created element (with Roo.factory())
16098      * @type {Roo.Layout}
16099      */
16100     el  : false,
16101     
16102     /**
16103      * @property el
16104      * for BC  - use el in new code
16105      * @type {Roo.Layout}
16106      */
16107     panel : false,
16108     
16109     /**
16110      * @property layout
16111      * for BC  - use el in new code
16112      * @type {Roo.Layout}
16113      */
16114     layout : false,
16115     
16116      /**
16117      * @cfg {Function|boolean} disabled
16118      * If this module is disabled by some rule, return true from the funtion
16119      */
16120     disabled : false,
16121     
16122     /**
16123      * @cfg {String} parent 
16124      * Name of parent element which it get xtype added to..
16125      */
16126     parent: false,
16127     
16128     /**
16129      * @cfg {String} order
16130      * Used to set the order in which elements are created (usefull for multiple tabs)
16131      */
16132     
16133     order : false,
16134     /**
16135      * @cfg {String} name
16136      * String to display while loading.
16137      */
16138     name : false,
16139     /**
16140      * @cfg {String} region
16141      * Region to render component to (defaults to center)
16142      */
16143     region : 'center',
16144     
16145     /**
16146      * @cfg {Array} items
16147      * A single item array - the first element is the root of the tree..
16148      * It's done this way to stay compatible with the Xtype system...
16149      */
16150     items : false,
16151     
16152     /**
16153      * @property _tree
16154      * The method that retuns the tree of parts that make up this compoennt 
16155      * @type {function}
16156      */
16157     _tree  : false,
16158     
16159      /**
16160      * render
16161      * render element to dom or tree
16162      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16163      */
16164     
16165     render : function(el)
16166     {
16167         
16168         el = el || false;
16169         var hp = this.parent ? 1 : 0;
16170         Roo.debug &&  Roo.log(this);
16171         
16172         var tree = this._tree ? this._tree() : this.tree();
16173
16174         
16175         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16176             // if parent is a '#.....' string, then let's use that..
16177             var ename = this.parent.substr(1);
16178             this.parent = false;
16179             Roo.debug && Roo.log(ename);
16180             switch (ename) {
16181                 case 'bootstrap-body':
16182                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16183                         // this is the BorderLayout standard?
16184                        this.parent = { el : true };
16185                        break;
16186                     }
16187                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16188                         // need to insert stuff...
16189                         this.parent =  {
16190                              el : new Roo.bootstrap.layout.Border({
16191                                  el : document.body, 
16192                      
16193                                  center: {
16194                                     titlebar: false,
16195                                     autoScroll:false,
16196                                     closeOnTab: true,
16197                                     tabPosition: 'top',
16198                                       //resizeTabs: true,
16199                                     alwaysShowTabs: true,
16200                                     hideTabs: false
16201                                      //minTabWidth: 140
16202                                  }
16203                              })
16204                         
16205                          };
16206                          break;
16207                     }
16208                          
16209                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16210                         this.parent = { el :  new  Roo.bootstrap.Body() };
16211                         Roo.debug && Roo.log("setting el to doc body");
16212                          
16213                     } else {
16214                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16215                     }
16216                     break;
16217                 case 'bootstrap':
16218                     this.parent = { el : true};
16219                     // fall through
16220                 default:
16221                     el = Roo.get(ename);
16222                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16223                         this.parent = { el : true};
16224                     }
16225                     
16226                     break;
16227             }
16228                 
16229             
16230             if (!el && !this.parent) {
16231                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16232                 return;
16233             }
16234         }
16235         
16236         Roo.debug && Roo.log("EL:");
16237         Roo.debug && Roo.log(el);
16238         Roo.debug && Roo.log("this.parent.el:");
16239         Roo.debug && Roo.log(this.parent.el);
16240         
16241
16242         // altertive root elements ??? - we need a better way to indicate these.
16243         var is_alt = Roo.XComponent.is_alt ||
16244                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16245                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16246                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16247         
16248         
16249         
16250         if (!this.parent && is_alt) {
16251             //el = Roo.get(document.body);
16252             this.parent = { el : true };
16253         }
16254             
16255             
16256         
16257         if (!this.parent) {
16258             
16259             Roo.debug && Roo.log("no parent - creating one");
16260             
16261             el = el ? Roo.get(el) : false;      
16262             
16263             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16264                 
16265                 this.parent =  {
16266                     el : new Roo.bootstrap.layout.Border({
16267                         el: el || document.body,
16268                     
16269                         center: {
16270                             titlebar: false,
16271                             autoScroll:false,
16272                             closeOnTab: true,
16273                             tabPosition: 'top',
16274                              //resizeTabs: true,
16275                             alwaysShowTabs: false,
16276                             hideTabs: true,
16277                             minTabWidth: 140,
16278                             overflow: 'visible'
16279                          }
16280                      })
16281                 };
16282             } else {
16283             
16284                 // it's a top level one..
16285                 this.parent =  {
16286                     el : new Roo.BorderLayout(el || document.body, {
16287                         center: {
16288                             titlebar: false,
16289                             autoScroll:false,
16290                             closeOnTab: true,
16291                             tabPosition: 'top',
16292                              //resizeTabs: true,
16293                             alwaysShowTabs: el && hp? false :  true,
16294                             hideTabs: el || !hp ? true :  false,
16295                             minTabWidth: 140
16296                          }
16297                     })
16298                 };
16299             }
16300         }
16301         
16302         if (!this.parent.el) {
16303                 // probably an old style ctor, which has been disabled.
16304                 return;
16305
16306         }
16307                 // The 'tree' method is  '_tree now' 
16308             
16309         tree.region = tree.region || this.region;
16310         var is_body = false;
16311         if (this.parent.el === true) {
16312             // bootstrap... - body..
16313             if (el) {
16314                 tree.el = el;
16315             }
16316             this.parent.el = Roo.factory(tree);
16317             is_body = true;
16318         }
16319         
16320         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16321         this.fireEvent('built', this);
16322         
16323         this.panel = this.el;
16324         this.layout = this.panel.layout;
16325         this.parentLayout = this.parent.layout  || false;  
16326          
16327     }
16328     
16329 });
16330
16331 Roo.apply(Roo.XComponent, {
16332     /**
16333      * @property  hideProgress
16334      * true to disable the building progress bar.. usefull on single page renders.
16335      * @type Boolean
16336      */
16337     hideProgress : false,
16338     /**
16339      * @property  buildCompleted
16340      * True when the builder has completed building the interface.
16341      * @type Boolean
16342      */
16343     buildCompleted : false,
16344      
16345     /**
16346      * @property  topModule
16347      * the upper most module - uses document.element as it's constructor.
16348      * @type Object
16349      */
16350      
16351     topModule  : false,
16352       
16353     /**
16354      * @property  modules
16355      * array of modules to be created by registration system.
16356      * @type {Array} of Roo.XComponent
16357      */
16358     
16359     modules : [],
16360     /**
16361      * @property  elmodules
16362      * array of modules to be created by which use #ID 
16363      * @type {Array} of Roo.XComponent
16364      */
16365      
16366     elmodules : [],
16367
16368      /**
16369      * @property  is_alt
16370      * Is an alternative Root - normally used by bootstrap or other systems,
16371      *    where the top element in the tree can wrap 'body' 
16372      * @type {boolean}  (default false)
16373      */
16374      
16375     is_alt : false,
16376     /**
16377      * @property  build_from_html
16378      * Build elements from html - used by bootstrap HTML stuff 
16379      *    - this is cleared after build is completed
16380      * @type {boolean}    (default false)
16381      */
16382      
16383     build_from_html : false,
16384     /**
16385      * Register components to be built later.
16386      *
16387      * This solves the following issues
16388      * - Building is not done on page load, but after an authentication process has occured.
16389      * - Interface elements are registered on page load
16390      * - Parent Interface elements may not be loaded before child, so this handles that..
16391      * 
16392      *
16393      * example:
16394      * 
16395      * MyApp.register({
16396           order : '000001',
16397           module : 'Pman.Tab.projectMgr',
16398           region : 'center',
16399           parent : 'Pman.layout',
16400           disabled : false,  // or use a function..
16401         })
16402      
16403      * * @param {Object} details about module
16404      */
16405     register : function(obj) {
16406                 
16407         Roo.XComponent.event.fireEvent('register', obj);
16408         switch(typeof(obj.disabled) ) {
16409                 
16410             case 'undefined':
16411                 break;
16412             
16413             case 'function':
16414                 if ( obj.disabled() ) {
16415                         return;
16416                 }
16417                 break;
16418             
16419             default:
16420                 if (obj.disabled) {
16421                         return;
16422                 }
16423                 break;
16424         }
16425                 
16426         this.modules.push(obj);
16427          
16428     },
16429     /**
16430      * convert a string to an object..
16431      * eg. 'AAA.BBB' -> finds AAA.BBB
16432
16433      */
16434     
16435     toObject : function(str)
16436     {
16437         if (!str || typeof(str) == 'object') {
16438             return str;
16439         }
16440         if (str.substring(0,1) == '#') {
16441             return str;
16442         }
16443
16444         var ar = str.split('.');
16445         var rt, o;
16446         rt = ar.shift();
16447             /** eval:var:o */
16448         try {
16449             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16450         } catch (e) {
16451             throw "Module not found : " + str;
16452         }
16453         
16454         if (o === false) {
16455             throw "Module not found : " + str;
16456         }
16457         Roo.each(ar, function(e) {
16458             if (typeof(o[e]) == 'undefined') {
16459                 throw "Module not found : " + str;
16460             }
16461             o = o[e];
16462         });
16463         
16464         return o;
16465         
16466     },
16467     
16468     
16469     /**
16470      * move modules into their correct place in the tree..
16471      * 
16472      */
16473     preBuild : function ()
16474     {
16475         var _t = this;
16476         Roo.each(this.modules , function (obj)
16477         {
16478             Roo.XComponent.event.fireEvent('beforebuild', obj);
16479             
16480             var opar = obj.parent;
16481             try { 
16482                 obj.parent = this.toObject(opar);
16483             } catch(e) {
16484                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16485                 return;
16486             }
16487             
16488             if (!obj.parent) {
16489                 Roo.debug && Roo.log("GOT top level module");
16490                 Roo.debug && Roo.log(obj);
16491                 obj.modules = new Roo.util.MixedCollection(false, 
16492                     function(o) { return o.order + '' }
16493                 );
16494                 this.topModule = obj;
16495                 return;
16496             }
16497                         // parent is a string (usually a dom element name..)
16498             if (typeof(obj.parent) == 'string') {
16499                 this.elmodules.push(obj);
16500                 return;
16501             }
16502             if (obj.parent.constructor != Roo.XComponent) {
16503                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16504             }
16505             if (!obj.parent.modules) {
16506                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16507                     function(o) { return o.order + '' }
16508                 );
16509             }
16510             if (obj.parent.disabled) {
16511                 obj.disabled = true;
16512             }
16513             obj.parent.modules.add(obj);
16514         }, this);
16515     },
16516     
16517      /**
16518      * make a list of modules to build.
16519      * @return {Array} list of modules. 
16520      */ 
16521     
16522     buildOrder : function()
16523     {
16524         var _this = this;
16525         var cmp = function(a,b) {   
16526             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16527         };
16528         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16529             throw "No top level modules to build";
16530         }
16531         
16532         // make a flat list in order of modules to build.
16533         var mods = this.topModule ? [ this.topModule ] : [];
16534                 
16535         
16536         // elmodules (is a list of DOM based modules )
16537         Roo.each(this.elmodules, function(e) {
16538             mods.push(e);
16539             if (!this.topModule &&
16540                 typeof(e.parent) == 'string' &&
16541                 e.parent.substring(0,1) == '#' &&
16542                 Roo.get(e.parent.substr(1))
16543                ) {
16544                 
16545                 _this.topModule = e;
16546             }
16547             
16548         });
16549
16550         
16551         // add modules to their parents..
16552         var addMod = function(m) {
16553             Roo.debug && Roo.log("build Order: add: " + m.name);
16554                 
16555             mods.push(m);
16556             if (m.modules && !m.disabled) {
16557                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16558                 m.modules.keySort('ASC',  cmp );
16559                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16560     
16561                 m.modules.each(addMod);
16562             } else {
16563                 Roo.debug && Roo.log("build Order: no child modules");
16564             }
16565             // not sure if this is used any more..
16566             if (m.finalize) {
16567                 m.finalize.name = m.name + " (clean up) ";
16568                 mods.push(m.finalize);
16569             }
16570             
16571         }
16572         if (this.topModule && this.topModule.modules) { 
16573             this.topModule.modules.keySort('ASC',  cmp );
16574             this.topModule.modules.each(addMod);
16575         } 
16576         return mods;
16577     },
16578     
16579      /**
16580      * Build the registered modules.
16581      * @param {Object} parent element.
16582      * @param {Function} optional method to call after module has been added.
16583      * 
16584      */ 
16585    
16586     build : function(opts) 
16587     {
16588         
16589         if (typeof(opts) != 'undefined') {
16590             Roo.apply(this,opts);
16591         }
16592         
16593         this.preBuild();
16594         var mods = this.buildOrder();
16595       
16596         //this.allmods = mods;
16597         //Roo.debug && Roo.log(mods);
16598         //return;
16599         if (!mods.length) { // should not happen
16600             throw "NO modules!!!";
16601         }
16602         
16603         
16604         var msg = "Building Interface...";
16605         // flash it up as modal - so we store the mask!?
16606         if (!this.hideProgress && Roo.MessageBox) {
16607             Roo.MessageBox.show({ title: 'loading' });
16608             Roo.MessageBox.show({
16609                title: "Please wait...",
16610                msg: msg,
16611                width:450,
16612                progress:true,
16613                buttons : false,
16614                closable:false,
16615                modal: false
16616               
16617             });
16618         }
16619         var total = mods.length;
16620         
16621         var _this = this;
16622         var progressRun = function() {
16623             if (!mods.length) {
16624                 Roo.debug && Roo.log('hide?');
16625                 if (!this.hideProgress && Roo.MessageBox) {
16626                     Roo.MessageBox.hide();
16627                 }
16628                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16629                 
16630                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16631                 
16632                 // THE END...
16633                 return false;   
16634             }
16635             
16636             var m = mods.shift();
16637             
16638             
16639             Roo.debug && Roo.log(m);
16640             // not sure if this is supported any more.. - modules that are are just function
16641             if (typeof(m) == 'function') { 
16642                 m.call(this);
16643                 return progressRun.defer(10, _this);
16644             } 
16645             
16646             
16647             msg = "Building Interface " + (total  - mods.length) + 
16648                     " of " + total + 
16649                     (m.name ? (' - ' + m.name) : '');
16650                         Roo.debug && Roo.log(msg);
16651             if (!_this.hideProgress &&  Roo.MessageBox) { 
16652                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16653             }
16654             
16655          
16656             // is the module disabled?
16657             var disabled = (typeof(m.disabled) == 'function') ?
16658                 m.disabled.call(m.module.disabled) : m.disabled;    
16659             
16660             
16661             if (disabled) {
16662                 return progressRun(); // we do not update the display!
16663             }
16664             
16665             // now build 
16666             
16667                         
16668                         
16669             m.render();
16670             // it's 10 on top level, and 1 on others??? why...
16671             return progressRun.defer(10, _this);
16672              
16673         }
16674         progressRun.defer(1, _this);
16675      
16676         
16677         
16678     },
16679     /**
16680      * Overlay a set of modified strings onto a component
16681      * This is dependant on our builder exporting the strings and 'named strings' elements.
16682      * 
16683      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
16684      * @param {Object} associative array of 'named' string and it's new value.
16685      * 
16686      */
16687         overlayStrings : function( component, strings )
16688     {
16689         if (typeof(component['_named_strings']) == 'undefined') {
16690             throw "ERROR: component does not have _named_strings";
16691         }
16692         for ( var k in strings ) {
16693             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
16694             if (md !== false) {
16695                 component['_strings'][md] = strings[k];
16696             } else {
16697                 Roo.log('could not find named string: ' + k + ' in');
16698                 Roo.log(component);
16699             }
16700             
16701         }
16702         
16703     },
16704     
16705         
16706         /**
16707          * Event Object.
16708          *
16709          *
16710          */
16711         event: false, 
16712     /**
16713          * wrapper for event.on - aliased later..  
16714          * Typically use to register a event handler for register:
16715          *
16716          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16717          *
16718          */
16719     on : false
16720    
16721     
16722     
16723 });
16724
16725 Roo.XComponent.event = new Roo.util.Observable({
16726                 events : { 
16727                         /**
16728                          * @event register
16729                          * Fires when an Component is registered,
16730                          * set the disable property on the Component to stop registration.
16731                          * @param {Roo.XComponent} c the component being registerd.
16732                          * 
16733                          */
16734                         'register' : true,
16735             /**
16736                          * @event beforebuild
16737                          * Fires before each Component is built
16738                          * can be used to apply permissions.
16739                          * @param {Roo.XComponent} c the component being registerd.
16740                          * 
16741                          */
16742                         'beforebuild' : true,
16743                         /**
16744                          * @event buildcomplete
16745                          * Fires on the top level element when all elements have been built
16746                          * @param {Roo.XComponent} the top level component.
16747                          */
16748                         'buildcomplete' : true
16749                         
16750                 }
16751 });
16752
16753 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16754  //
16755  /**
16756  * marked - a markdown parser
16757  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16758  * https://github.com/chjj/marked
16759  */
16760
16761
16762 /**
16763  *
16764  * Roo.Markdown - is a very crude wrapper around marked..
16765  *
16766  * usage:
16767  * 
16768  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16769  * 
16770  * Note: move the sample code to the bottom of this
16771  * file before uncommenting it.
16772  *
16773  */
16774
16775 Roo.Markdown = {};
16776 Roo.Markdown.toHtml = function(text) {
16777     
16778     var c = new Roo.Markdown.marked.setOptions({
16779             renderer: new Roo.Markdown.marked.Renderer(),
16780             gfm: true,
16781             tables: true,
16782             breaks: false,
16783             pedantic: false,
16784             sanitize: false,
16785             smartLists: true,
16786             smartypants: false
16787           });
16788     // A FEW HACKS!!?
16789     
16790     text = text.replace(/\\\n/g,' ');
16791     return Roo.Markdown.marked(text);
16792 };
16793 //
16794 // converter
16795 //
16796 // Wraps all "globals" so that the only thing
16797 // exposed is makeHtml().
16798 //
16799 (function() {
16800     
16801     /**
16802      * Block-Level Grammar
16803      */
16804     
16805     var block = {
16806       newline: /^\n+/,
16807       code: /^( {4}[^\n]+\n*)+/,
16808       fences: noop,
16809       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16810       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16811       nptable: noop,
16812       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16813       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16814       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16815       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16816       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16817       table: noop,
16818       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16819       text: /^[^\n]+/
16820     };
16821     
16822     block.bullet = /(?:[*+-]|\d+\.)/;
16823     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16824     block.item = replace(block.item, 'gm')
16825       (/bull/g, block.bullet)
16826       ();
16827     
16828     block.list = replace(block.list)
16829       (/bull/g, block.bullet)
16830       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16831       ('def', '\\n+(?=' + block.def.source + ')')
16832       ();
16833     
16834     block.blockquote = replace(block.blockquote)
16835       ('def', block.def)
16836       ();
16837     
16838     block._tag = '(?!(?:'
16839       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16840       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16841       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16842     
16843     block.html = replace(block.html)
16844       ('comment', /<!--[\s\S]*?-->/)
16845       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16846       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16847       (/tag/g, block._tag)
16848       ();
16849     
16850     block.paragraph = replace(block.paragraph)
16851       ('hr', block.hr)
16852       ('heading', block.heading)
16853       ('lheading', block.lheading)
16854       ('blockquote', block.blockquote)
16855       ('tag', '<' + block._tag)
16856       ('def', block.def)
16857       ();
16858     
16859     /**
16860      * Normal Block Grammar
16861      */
16862     
16863     block.normal = merge({}, block);
16864     
16865     /**
16866      * GFM Block Grammar
16867      */
16868     
16869     block.gfm = merge({}, block.normal, {
16870       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16871       paragraph: /^/,
16872       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16873     });
16874     
16875     block.gfm.paragraph = replace(block.paragraph)
16876       ('(?!', '(?!'
16877         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16878         + block.list.source.replace('\\1', '\\3') + '|')
16879       ();
16880     
16881     /**
16882      * GFM + Tables Block Grammar
16883      */
16884     
16885     block.tables = merge({}, block.gfm, {
16886       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16887       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16888     });
16889     
16890     /**
16891      * Block Lexer
16892      */
16893     
16894     function Lexer(options) {
16895       this.tokens = [];
16896       this.tokens.links = {};
16897       this.options = options || marked.defaults;
16898       this.rules = block.normal;
16899     
16900       if (this.options.gfm) {
16901         if (this.options.tables) {
16902           this.rules = block.tables;
16903         } else {
16904           this.rules = block.gfm;
16905         }
16906       }
16907     }
16908     
16909     /**
16910      * Expose Block Rules
16911      */
16912     
16913     Lexer.rules = block;
16914     
16915     /**
16916      * Static Lex Method
16917      */
16918     
16919     Lexer.lex = function(src, options) {
16920       var lexer = new Lexer(options);
16921       return lexer.lex(src);
16922     };
16923     
16924     /**
16925      * Preprocessing
16926      */
16927     
16928     Lexer.prototype.lex = function(src) {
16929       src = src
16930         .replace(/\r\n|\r/g, '\n')
16931         .replace(/\t/g, '    ')
16932         .replace(/\u00a0/g, ' ')
16933         .replace(/\u2424/g, '\n');
16934     
16935       return this.token(src, true);
16936     };
16937     
16938     /**
16939      * Lexing
16940      */
16941     
16942     Lexer.prototype.token = function(src, top, bq) {
16943       var src = src.replace(/^ +$/gm, '')
16944         , next
16945         , loose
16946         , cap
16947         , bull
16948         , b
16949         , item
16950         , space
16951         , i
16952         , l;
16953     
16954       while (src) {
16955         // newline
16956         if (cap = this.rules.newline.exec(src)) {
16957           src = src.substring(cap[0].length);
16958           if (cap[0].length > 1) {
16959             this.tokens.push({
16960               type: 'space'
16961             });
16962           }
16963         }
16964     
16965         // code
16966         if (cap = this.rules.code.exec(src)) {
16967           src = src.substring(cap[0].length);
16968           cap = cap[0].replace(/^ {4}/gm, '');
16969           this.tokens.push({
16970             type: 'code',
16971             text: !this.options.pedantic
16972               ? cap.replace(/\n+$/, '')
16973               : cap
16974           });
16975           continue;
16976         }
16977     
16978         // fences (gfm)
16979         if (cap = this.rules.fences.exec(src)) {
16980           src = src.substring(cap[0].length);
16981           this.tokens.push({
16982             type: 'code',
16983             lang: cap[2],
16984             text: cap[3] || ''
16985           });
16986           continue;
16987         }
16988     
16989         // heading
16990         if (cap = this.rules.heading.exec(src)) {
16991           src = src.substring(cap[0].length);
16992           this.tokens.push({
16993             type: 'heading',
16994             depth: cap[1].length,
16995             text: cap[2]
16996           });
16997           continue;
16998         }
16999     
17000         // table no leading pipe (gfm)
17001         if (top && (cap = this.rules.nptable.exec(src))) {
17002           src = src.substring(cap[0].length);
17003     
17004           item = {
17005             type: 'table',
17006             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17007             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17008             cells: cap[3].replace(/\n$/, '').split('\n')
17009           };
17010     
17011           for (i = 0; i < item.align.length; i++) {
17012             if (/^ *-+: *$/.test(item.align[i])) {
17013               item.align[i] = 'right';
17014             } else if (/^ *:-+: *$/.test(item.align[i])) {
17015               item.align[i] = 'center';
17016             } else if (/^ *:-+ *$/.test(item.align[i])) {
17017               item.align[i] = 'left';
17018             } else {
17019               item.align[i] = null;
17020             }
17021           }
17022     
17023           for (i = 0; i < item.cells.length; i++) {
17024             item.cells[i] = item.cells[i].split(/ *\| */);
17025           }
17026     
17027           this.tokens.push(item);
17028     
17029           continue;
17030         }
17031     
17032         // lheading
17033         if (cap = this.rules.lheading.exec(src)) {
17034           src = src.substring(cap[0].length);
17035           this.tokens.push({
17036             type: 'heading',
17037             depth: cap[2] === '=' ? 1 : 2,
17038             text: cap[1]
17039           });
17040           continue;
17041         }
17042     
17043         // hr
17044         if (cap = this.rules.hr.exec(src)) {
17045           src = src.substring(cap[0].length);
17046           this.tokens.push({
17047             type: 'hr'
17048           });
17049           continue;
17050         }
17051     
17052         // blockquote
17053         if (cap = this.rules.blockquote.exec(src)) {
17054           src = src.substring(cap[0].length);
17055     
17056           this.tokens.push({
17057             type: 'blockquote_start'
17058           });
17059     
17060           cap = cap[0].replace(/^ *> ?/gm, '');
17061     
17062           // Pass `top` to keep the current
17063           // "toplevel" state. This is exactly
17064           // how markdown.pl works.
17065           this.token(cap, top, true);
17066     
17067           this.tokens.push({
17068             type: 'blockquote_end'
17069           });
17070     
17071           continue;
17072         }
17073     
17074         // list
17075         if (cap = this.rules.list.exec(src)) {
17076           src = src.substring(cap[0].length);
17077           bull = cap[2];
17078     
17079           this.tokens.push({
17080             type: 'list_start',
17081             ordered: bull.length > 1
17082           });
17083     
17084           // Get each top-level item.
17085           cap = cap[0].match(this.rules.item);
17086     
17087           next = false;
17088           l = cap.length;
17089           i = 0;
17090     
17091           for (; i < l; i++) {
17092             item = cap[i];
17093     
17094             // Remove the list item's bullet
17095             // so it is seen as the next token.
17096             space = item.length;
17097             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17098     
17099             // Outdent whatever the
17100             // list item contains. Hacky.
17101             if (~item.indexOf('\n ')) {
17102               space -= item.length;
17103               item = !this.options.pedantic
17104                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17105                 : item.replace(/^ {1,4}/gm, '');
17106             }
17107     
17108             // Determine whether the next list item belongs here.
17109             // Backpedal if it does not belong in this list.
17110             if (this.options.smartLists && i !== l - 1) {
17111               b = block.bullet.exec(cap[i + 1])[0];
17112               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17113                 src = cap.slice(i + 1).join('\n') + src;
17114                 i = l - 1;
17115               }
17116             }
17117     
17118             // Determine whether item is loose or not.
17119             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17120             // for discount behavior.
17121             loose = next || /\n\n(?!\s*$)/.test(item);
17122             if (i !== l - 1) {
17123               next = item.charAt(item.length - 1) === '\n';
17124               if (!loose) { loose = next; }
17125             }
17126     
17127             this.tokens.push({
17128               type: loose
17129                 ? 'loose_item_start'
17130                 : 'list_item_start'
17131             });
17132     
17133             // Recurse.
17134             this.token(item, false, bq);
17135     
17136             this.tokens.push({
17137               type: 'list_item_end'
17138             });
17139           }
17140     
17141           this.tokens.push({
17142             type: 'list_end'
17143           });
17144     
17145           continue;
17146         }
17147     
17148         // html
17149         if (cap = this.rules.html.exec(src)) {
17150           src = src.substring(cap[0].length);
17151           this.tokens.push({
17152             type: this.options.sanitize
17153               ? 'paragraph'
17154               : 'html',
17155             pre: !this.options.sanitizer
17156               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17157             text: cap[0]
17158           });
17159           continue;
17160         }
17161     
17162         // def
17163         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17164           src = src.substring(cap[0].length);
17165           this.tokens.links[cap[1].toLowerCase()] = {
17166             href: cap[2],
17167             title: cap[3]
17168           };
17169           continue;
17170         }
17171     
17172         // table (gfm)
17173         if (top && (cap = this.rules.table.exec(src))) {
17174           src = src.substring(cap[0].length);
17175     
17176           item = {
17177             type: 'table',
17178             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17179             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17180             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17181           };
17182     
17183           for (i = 0; i < item.align.length; i++) {
17184             if (/^ *-+: *$/.test(item.align[i])) {
17185               item.align[i] = 'right';
17186             } else if (/^ *:-+: *$/.test(item.align[i])) {
17187               item.align[i] = 'center';
17188             } else if (/^ *:-+ *$/.test(item.align[i])) {
17189               item.align[i] = 'left';
17190             } else {
17191               item.align[i] = null;
17192             }
17193           }
17194     
17195           for (i = 0; i < item.cells.length; i++) {
17196             item.cells[i] = item.cells[i]
17197               .replace(/^ *\| *| *\| *$/g, '')
17198               .split(/ *\| */);
17199           }
17200     
17201           this.tokens.push(item);
17202     
17203           continue;
17204         }
17205     
17206         // top-level paragraph
17207         if (top && (cap = this.rules.paragraph.exec(src))) {
17208           src = src.substring(cap[0].length);
17209           this.tokens.push({
17210             type: 'paragraph',
17211             text: cap[1].charAt(cap[1].length - 1) === '\n'
17212               ? cap[1].slice(0, -1)
17213               : cap[1]
17214           });
17215           continue;
17216         }
17217     
17218         // text
17219         if (cap = this.rules.text.exec(src)) {
17220           // Top-level should never reach here.
17221           src = src.substring(cap[0].length);
17222           this.tokens.push({
17223             type: 'text',
17224             text: cap[0]
17225           });
17226           continue;
17227         }
17228     
17229         if (src) {
17230           throw new
17231             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17232         }
17233       }
17234     
17235       return this.tokens;
17236     };
17237     
17238     /**
17239      * Inline-Level Grammar
17240      */
17241     
17242     var inline = {
17243       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17244       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17245       url: noop,
17246       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17247       link: /^!?\[(inside)\]\(href\)/,
17248       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17249       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17250       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17251       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17252       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17253       br: /^ {2,}\n(?!\s*$)/,
17254       del: noop,
17255       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17256     };
17257     
17258     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17259     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17260     
17261     inline.link = replace(inline.link)
17262       ('inside', inline._inside)
17263       ('href', inline._href)
17264       ();
17265     
17266     inline.reflink = replace(inline.reflink)
17267       ('inside', inline._inside)
17268       ();
17269     
17270     /**
17271      * Normal Inline Grammar
17272      */
17273     
17274     inline.normal = merge({}, inline);
17275     
17276     /**
17277      * Pedantic Inline Grammar
17278      */
17279     
17280     inline.pedantic = merge({}, inline.normal, {
17281       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17282       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17283     });
17284     
17285     /**
17286      * GFM Inline Grammar
17287      */
17288     
17289     inline.gfm = merge({}, inline.normal, {
17290       escape: replace(inline.escape)('])', '~|])')(),
17291       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17292       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17293       text: replace(inline.text)
17294         (']|', '~]|')
17295         ('|', '|https?://|')
17296         ()
17297     });
17298     
17299     /**
17300      * GFM + Line Breaks Inline Grammar
17301      */
17302     
17303     inline.breaks = merge({}, inline.gfm, {
17304       br: replace(inline.br)('{2,}', '*')(),
17305       text: replace(inline.gfm.text)('{2,}', '*')()
17306     });
17307     
17308     /**
17309      * Inline Lexer & Compiler
17310      */
17311     
17312     function InlineLexer(links, options) {
17313       this.options = options || marked.defaults;
17314       this.links = links;
17315       this.rules = inline.normal;
17316       this.renderer = this.options.renderer || new Renderer;
17317       this.renderer.options = this.options;
17318     
17319       if (!this.links) {
17320         throw new
17321           Error('Tokens array requires a `links` property.');
17322       }
17323     
17324       if (this.options.gfm) {
17325         if (this.options.breaks) {
17326           this.rules = inline.breaks;
17327         } else {
17328           this.rules = inline.gfm;
17329         }
17330       } else if (this.options.pedantic) {
17331         this.rules = inline.pedantic;
17332       }
17333     }
17334     
17335     /**
17336      * Expose Inline Rules
17337      */
17338     
17339     InlineLexer.rules = inline;
17340     
17341     /**
17342      * Static Lexing/Compiling Method
17343      */
17344     
17345     InlineLexer.output = function(src, links, options) {
17346       var inline = new InlineLexer(links, options);
17347       return inline.output(src);
17348     };
17349     
17350     /**
17351      * Lexing/Compiling
17352      */
17353     
17354     InlineLexer.prototype.output = function(src) {
17355       var out = ''
17356         , link
17357         , text
17358         , href
17359         , cap;
17360     
17361       while (src) {
17362         // escape
17363         if (cap = this.rules.escape.exec(src)) {
17364           src = src.substring(cap[0].length);
17365           out += cap[1];
17366           continue;
17367         }
17368     
17369         // autolink
17370         if (cap = this.rules.autolink.exec(src)) {
17371           src = src.substring(cap[0].length);
17372           if (cap[2] === '@') {
17373             text = cap[1].charAt(6) === ':'
17374               ? this.mangle(cap[1].substring(7))
17375               : this.mangle(cap[1]);
17376             href = this.mangle('mailto:') + text;
17377           } else {
17378             text = escape(cap[1]);
17379             href = text;
17380           }
17381           out += this.renderer.link(href, null, text);
17382           continue;
17383         }
17384     
17385         // url (gfm)
17386         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17387           src = src.substring(cap[0].length);
17388           text = escape(cap[1]);
17389           href = text;
17390           out += this.renderer.link(href, null, text);
17391           continue;
17392         }
17393     
17394         // tag
17395         if (cap = this.rules.tag.exec(src)) {
17396           if (!this.inLink && /^<a /i.test(cap[0])) {
17397             this.inLink = true;
17398           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17399             this.inLink = false;
17400           }
17401           src = src.substring(cap[0].length);
17402           out += this.options.sanitize
17403             ? this.options.sanitizer
17404               ? this.options.sanitizer(cap[0])
17405               : escape(cap[0])
17406             : cap[0];
17407           continue;
17408         }
17409     
17410         // link
17411         if (cap = this.rules.link.exec(src)) {
17412           src = src.substring(cap[0].length);
17413           this.inLink = true;
17414           out += this.outputLink(cap, {
17415             href: cap[2],
17416             title: cap[3]
17417           });
17418           this.inLink = false;
17419           continue;
17420         }
17421     
17422         // reflink, nolink
17423         if ((cap = this.rules.reflink.exec(src))
17424             || (cap = this.rules.nolink.exec(src))) {
17425           src = src.substring(cap[0].length);
17426           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17427           link = this.links[link.toLowerCase()];
17428           if (!link || !link.href) {
17429             out += cap[0].charAt(0);
17430             src = cap[0].substring(1) + src;
17431             continue;
17432           }
17433           this.inLink = true;
17434           out += this.outputLink(cap, link);
17435           this.inLink = false;
17436           continue;
17437         }
17438     
17439         // strong
17440         if (cap = this.rules.strong.exec(src)) {
17441           src = src.substring(cap[0].length);
17442           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17443           continue;
17444         }
17445     
17446         // em
17447         if (cap = this.rules.em.exec(src)) {
17448           src = src.substring(cap[0].length);
17449           out += this.renderer.em(this.output(cap[2] || cap[1]));
17450           continue;
17451         }
17452     
17453         // code
17454         if (cap = this.rules.code.exec(src)) {
17455           src = src.substring(cap[0].length);
17456           out += this.renderer.codespan(escape(cap[2], true));
17457           continue;
17458         }
17459     
17460         // br
17461         if (cap = this.rules.br.exec(src)) {
17462           src = src.substring(cap[0].length);
17463           out += this.renderer.br();
17464           continue;
17465         }
17466     
17467         // del (gfm)
17468         if (cap = this.rules.del.exec(src)) {
17469           src = src.substring(cap[0].length);
17470           out += this.renderer.del(this.output(cap[1]));
17471           continue;
17472         }
17473     
17474         // text
17475         if (cap = this.rules.text.exec(src)) {
17476           src = src.substring(cap[0].length);
17477           out += this.renderer.text(escape(this.smartypants(cap[0])));
17478           continue;
17479         }
17480     
17481         if (src) {
17482           throw new
17483             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17484         }
17485       }
17486     
17487       return out;
17488     };
17489     
17490     /**
17491      * Compile Link
17492      */
17493     
17494     InlineLexer.prototype.outputLink = function(cap, link) {
17495       var href = escape(link.href)
17496         , title = link.title ? escape(link.title) : null;
17497     
17498       return cap[0].charAt(0) !== '!'
17499         ? this.renderer.link(href, title, this.output(cap[1]))
17500         : this.renderer.image(href, title, escape(cap[1]));
17501     };
17502     
17503     /**
17504      * Smartypants Transformations
17505      */
17506     
17507     InlineLexer.prototype.smartypants = function(text) {
17508       if (!this.options.smartypants)  { return text; }
17509       return text
17510         // em-dashes
17511         .replace(/---/g, '\u2014')
17512         // en-dashes
17513         .replace(/--/g, '\u2013')
17514         // opening singles
17515         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17516         // closing singles & apostrophes
17517         .replace(/'/g, '\u2019')
17518         // opening doubles
17519         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17520         // closing doubles
17521         .replace(/"/g, '\u201d')
17522         // ellipses
17523         .replace(/\.{3}/g, '\u2026');
17524     };
17525     
17526     /**
17527      * Mangle Links
17528      */
17529     
17530     InlineLexer.prototype.mangle = function(text) {
17531       if (!this.options.mangle) { return text; }
17532       var out = ''
17533         , l = text.length
17534         , i = 0
17535         , ch;
17536     
17537       for (; i < l; i++) {
17538         ch = text.charCodeAt(i);
17539         if (Math.random() > 0.5) {
17540           ch = 'x' + ch.toString(16);
17541         }
17542         out += '&#' + ch + ';';
17543       }
17544     
17545       return out;
17546     };
17547     
17548     /**
17549      * Renderer
17550      */
17551     
17552     function Renderer(options) {
17553       this.options = options || {};
17554     }
17555     
17556     Renderer.prototype.code = function(code, lang, escaped) {
17557       if (this.options.highlight) {
17558         var out = this.options.highlight(code, lang);
17559         if (out != null && out !== code) {
17560           escaped = true;
17561           code = out;
17562         }
17563       } else {
17564             // hack!!! - it's already escapeD?
17565             escaped = true;
17566       }
17567     
17568       if (!lang) {
17569         return '<pre><code>'
17570           + (escaped ? code : escape(code, true))
17571           + '\n</code></pre>';
17572       }
17573     
17574       return '<pre><code class="'
17575         + this.options.langPrefix
17576         + escape(lang, true)
17577         + '">'
17578         + (escaped ? code : escape(code, true))
17579         + '\n</code></pre>\n';
17580     };
17581     
17582     Renderer.prototype.blockquote = function(quote) {
17583       return '<blockquote>\n' + quote + '</blockquote>\n';
17584     };
17585     
17586     Renderer.prototype.html = function(html) {
17587       return html;
17588     };
17589     
17590     Renderer.prototype.heading = function(text, level, raw) {
17591       return '<h'
17592         + level
17593         + ' id="'
17594         + this.options.headerPrefix
17595         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17596         + '">'
17597         + text
17598         + '</h'
17599         + level
17600         + '>\n';
17601     };
17602     
17603     Renderer.prototype.hr = function() {
17604       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17605     };
17606     
17607     Renderer.prototype.list = function(body, ordered) {
17608       var type = ordered ? 'ol' : 'ul';
17609       return '<' + type + '>\n' + body + '</' + type + '>\n';
17610     };
17611     
17612     Renderer.prototype.listitem = function(text) {
17613       return '<li>' + text + '</li>\n';
17614     };
17615     
17616     Renderer.prototype.paragraph = function(text) {
17617       return '<p>' + text + '</p>\n';
17618     };
17619     
17620     Renderer.prototype.table = function(header, body) {
17621       return '<table class="table table-striped">\n'
17622         + '<thead>\n'
17623         + header
17624         + '</thead>\n'
17625         + '<tbody>\n'
17626         + body
17627         + '</tbody>\n'
17628         + '</table>\n';
17629     };
17630     
17631     Renderer.prototype.tablerow = function(content) {
17632       return '<tr>\n' + content + '</tr>\n';
17633     };
17634     
17635     Renderer.prototype.tablecell = function(content, flags) {
17636       var type = flags.header ? 'th' : 'td';
17637       var tag = flags.align
17638         ? '<' + type + ' style="text-align:' + flags.align + '">'
17639         : '<' + type + '>';
17640       return tag + content + '</' + type + '>\n';
17641     };
17642     
17643     // span level renderer
17644     Renderer.prototype.strong = function(text) {
17645       return '<strong>' + text + '</strong>';
17646     };
17647     
17648     Renderer.prototype.em = function(text) {
17649       return '<em>' + text + '</em>';
17650     };
17651     
17652     Renderer.prototype.codespan = function(text) {
17653       return '<code>' + text + '</code>';
17654     };
17655     
17656     Renderer.prototype.br = function() {
17657       return this.options.xhtml ? '<br/>' : '<br>';
17658     };
17659     
17660     Renderer.prototype.del = function(text) {
17661       return '<del>' + text + '</del>';
17662     };
17663     
17664     Renderer.prototype.link = function(href, title, text) {
17665       if (this.options.sanitize) {
17666         try {
17667           var prot = decodeURIComponent(unescape(href))
17668             .replace(/[^\w:]/g, '')
17669             .toLowerCase();
17670         } catch (e) {
17671           return '';
17672         }
17673         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17674           return '';
17675         }
17676       }
17677       var out = '<a href="' + href + '"';
17678       if (title) {
17679         out += ' title="' + title + '"';
17680       }
17681       out += '>' + text + '</a>';
17682       return out;
17683     };
17684     
17685     Renderer.prototype.image = function(href, title, text) {
17686       var out = '<img src="' + href + '" alt="' + text + '"';
17687       if (title) {
17688         out += ' title="' + title + '"';
17689       }
17690       out += this.options.xhtml ? '/>' : '>';
17691       return out;
17692     };
17693     
17694     Renderer.prototype.text = function(text) {
17695       return text;
17696     };
17697     
17698     /**
17699      * Parsing & Compiling
17700      */
17701     
17702     function Parser(options) {
17703       this.tokens = [];
17704       this.token = null;
17705       this.options = options || marked.defaults;
17706       this.options.renderer = this.options.renderer || new Renderer;
17707       this.renderer = this.options.renderer;
17708       this.renderer.options = this.options;
17709     }
17710     
17711     /**
17712      * Static Parse Method
17713      */
17714     
17715     Parser.parse = function(src, options, renderer) {
17716       var parser = new Parser(options, renderer);
17717       return parser.parse(src);
17718     };
17719     
17720     /**
17721      * Parse Loop
17722      */
17723     
17724     Parser.prototype.parse = function(src) {
17725       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17726       this.tokens = src.reverse();
17727     
17728       var out = '';
17729       while (this.next()) {
17730         out += this.tok();
17731       }
17732     
17733       return out;
17734     };
17735     
17736     /**
17737      * Next Token
17738      */
17739     
17740     Parser.prototype.next = function() {
17741       return this.token = this.tokens.pop();
17742     };
17743     
17744     /**
17745      * Preview Next Token
17746      */
17747     
17748     Parser.prototype.peek = function() {
17749       return this.tokens[this.tokens.length - 1] || 0;
17750     };
17751     
17752     /**
17753      * Parse Text Tokens
17754      */
17755     
17756     Parser.prototype.parseText = function() {
17757       var body = this.token.text;
17758     
17759       while (this.peek().type === 'text') {
17760         body += '\n' + this.next().text;
17761       }
17762     
17763       return this.inline.output(body);
17764     };
17765     
17766     /**
17767      * Parse Current Token
17768      */
17769     
17770     Parser.prototype.tok = function() {
17771       switch (this.token.type) {
17772         case 'space': {
17773           return '';
17774         }
17775         case 'hr': {
17776           return this.renderer.hr();
17777         }
17778         case 'heading': {
17779           return this.renderer.heading(
17780             this.inline.output(this.token.text),
17781             this.token.depth,
17782             this.token.text);
17783         }
17784         case 'code': {
17785           return this.renderer.code(this.token.text,
17786             this.token.lang,
17787             this.token.escaped);
17788         }
17789         case 'table': {
17790           var header = ''
17791             , body = ''
17792             , i
17793             , row
17794             , cell
17795             , flags
17796             , j;
17797     
17798           // header
17799           cell = '';
17800           for (i = 0; i < this.token.header.length; i++) {
17801             flags = { header: true, align: this.token.align[i] };
17802             cell += this.renderer.tablecell(
17803               this.inline.output(this.token.header[i]),
17804               { header: true, align: this.token.align[i] }
17805             );
17806           }
17807           header += this.renderer.tablerow(cell);
17808     
17809           for (i = 0; i < this.token.cells.length; i++) {
17810             row = this.token.cells[i];
17811     
17812             cell = '';
17813             for (j = 0; j < row.length; j++) {
17814               cell += this.renderer.tablecell(
17815                 this.inline.output(row[j]),
17816                 { header: false, align: this.token.align[j] }
17817               );
17818             }
17819     
17820             body += this.renderer.tablerow(cell);
17821           }
17822           return this.renderer.table(header, body);
17823         }
17824         case 'blockquote_start': {
17825           var body = '';
17826     
17827           while (this.next().type !== 'blockquote_end') {
17828             body += this.tok();
17829           }
17830     
17831           return this.renderer.blockquote(body);
17832         }
17833         case 'list_start': {
17834           var body = ''
17835             , ordered = this.token.ordered;
17836     
17837           while (this.next().type !== 'list_end') {
17838             body += this.tok();
17839           }
17840     
17841           return this.renderer.list(body, ordered);
17842         }
17843         case 'list_item_start': {
17844           var body = '';
17845     
17846           while (this.next().type !== 'list_item_end') {
17847             body += this.token.type === 'text'
17848               ? this.parseText()
17849               : this.tok();
17850           }
17851     
17852           return this.renderer.listitem(body);
17853         }
17854         case 'loose_item_start': {
17855           var body = '';
17856     
17857           while (this.next().type !== 'list_item_end') {
17858             body += this.tok();
17859           }
17860     
17861           return this.renderer.listitem(body);
17862         }
17863         case 'html': {
17864           var html = !this.token.pre && !this.options.pedantic
17865             ? this.inline.output(this.token.text)
17866             : this.token.text;
17867           return this.renderer.html(html);
17868         }
17869         case 'paragraph': {
17870           return this.renderer.paragraph(this.inline.output(this.token.text));
17871         }
17872         case 'text': {
17873           return this.renderer.paragraph(this.parseText());
17874         }
17875       }
17876     };
17877     
17878     /**
17879      * Helpers
17880      */
17881     
17882     function escape(html, encode) {
17883       return html
17884         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17885         .replace(/</g, '&lt;')
17886         .replace(/>/g, '&gt;')
17887         .replace(/"/g, '&quot;')
17888         .replace(/'/g, '&#39;');
17889     }
17890     
17891     function unescape(html) {
17892         // explicitly match decimal, hex, and named HTML entities 
17893       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17894         n = n.toLowerCase();
17895         if (n === 'colon') { return ':'; }
17896         if (n.charAt(0) === '#') {
17897           return n.charAt(1) === 'x'
17898             ? String.fromCharCode(parseInt(n.substring(2), 16))
17899             : String.fromCharCode(+n.substring(1));
17900         }
17901         return '';
17902       });
17903     }
17904     
17905     function replace(regex, opt) {
17906       regex = regex.source;
17907       opt = opt || '';
17908       return function self(name, val) {
17909         if (!name) { return new RegExp(regex, opt); }
17910         val = val.source || val;
17911         val = val.replace(/(^|[^\[])\^/g, '$1');
17912         regex = regex.replace(name, val);
17913         return self;
17914       };
17915     }
17916     
17917     function noop() {}
17918     noop.exec = noop;
17919     
17920     function merge(obj) {
17921       var i = 1
17922         , target
17923         , key;
17924     
17925       for (; i < arguments.length; i++) {
17926         target = arguments[i];
17927         for (key in target) {
17928           if (Object.prototype.hasOwnProperty.call(target, key)) {
17929             obj[key] = target[key];
17930           }
17931         }
17932       }
17933     
17934       return obj;
17935     }
17936     
17937     
17938     /**
17939      * Marked
17940      */
17941     
17942     function marked(src, opt, callback) {
17943       if (callback || typeof opt === 'function') {
17944         if (!callback) {
17945           callback = opt;
17946           opt = null;
17947         }
17948     
17949         opt = merge({}, marked.defaults, opt || {});
17950     
17951         var highlight = opt.highlight
17952           , tokens
17953           , pending
17954           , i = 0;
17955     
17956         try {
17957           tokens = Lexer.lex(src, opt)
17958         } catch (e) {
17959           return callback(e);
17960         }
17961     
17962         pending = tokens.length;
17963     
17964         var done = function(err) {
17965           if (err) {
17966             opt.highlight = highlight;
17967             return callback(err);
17968           }
17969     
17970           var out;
17971     
17972           try {
17973             out = Parser.parse(tokens, opt);
17974           } catch (e) {
17975             err = e;
17976           }
17977     
17978           opt.highlight = highlight;
17979     
17980           return err
17981             ? callback(err)
17982             : callback(null, out);
17983         };
17984     
17985         if (!highlight || highlight.length < 3) {
17986           return done();
17987         }
17988     
17989         delete opt.highlight;
17990     
17991         if (!pending) { return done(); }
17992     
17993         for (; i < tokens.length; i++) {
17994           (function(token) {
17995             if (token.type !== 'code') {
17996               return --pending || done();
17997             }
17998             return highlight(token.text, token.lang, function(err, code) {
17999               if (err) { return done(err); }
18000               if (code == null || code === token.text) {
18001                 return --pending || done();
18002               }
18003               token.text = code;
18004               token.escaped = true;
18005               --pending || done();
18006             });
18007           })(tokens[i]);
18008         }
18009     
18010         return;
18011       }
18012       try {
18013         if (opt) { opt = merge({}, marked.defaults, opt); }
18014         return Parser.parse(Lexer.lex(src, opt), opt);
18015       } catch (e) {
18016         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18017         if ((opt || marked.defaults).silent) {
18018           return '<p>An error occured:</p><pre>'
18019             + escape(e.message + '', true)
18020             + '</pre>';
18021         }
18022         throw e;
18023       }
18024     }
18025     
18026     /**
18027      * Options
18028      */
18029     
18030     marked.options =
18031     marked.setOptions = function(opt) {
18032       merge(marked.defaults, opt);
18033       return marked;
18034     };
18035     
18036     marked.defaults = {
18037       gfm: true,
18038       tables: true,
18039       breaks: false,
18040       pedantic: false,
18041       sanitize: false,
18042       sanitizer: null,
18043       mangle: true,
18044       smartLists: false,
18045       silent: false,
18046       highlight: null,
18047       langPrefix: 'lang-',
18048       smartypants: false,
18049       headerPrefix: '',
18050       renderer: new Renderer,
18051       xhtml: false
18052     };
18053     
18054     /**
18055      * Expose
18056      */
18057     
18058     marked.Parser = Parser;
18059     marked.parser = Parser.parse;
18060     
18061     marked.Renderer = Renderer;
18062     
18063     marked.Lexer = Lexer;
18064     marked.lexer = Lexer.lex;
18065     
18066     marked.InlineLexer = InlineLexer;
18067     marked.inlineLexer = InlineLexer.output;
18068     
18069     marked.parse = marked;
18070     
18071     Roo.Markdown.marked = marked;
18072
18073 })();/*
18074  * Based on:
18075  * Ext JS Library 1.1.1
18076  * Copyright(c) 2006-2007, Ext JS, LLC.
18077  *
18078  * Originally Released Under LGPL - original licence link has changed is not relivant.
18079  *
18080  * Fork - LGPL
18081  * <script type="text/javascript">
18082  */
18083
18084
18085
18086 /*
18087  * These classes are derivatives of the similarly named classes in the YUI Library.
18088  * The original license:
18089  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18090  * Code licensed under the BSD License:
18091  * http://developer.yahoo.net/yui/license.txt
18092  */
18093
18094 (function() {
18095
18096 var Event=Roo.EventManager;
18097 var Dom=Roo.lib.Dom;
18098
18099 /**
18100  * @class Roo.dd.DragDrop
18101  * @extends Roo.util.Observable
18102  * Defines the interface and base operation of items that that can be
18103  * dragged or can be drop targets.  It was designed to be extended, overriding
18104  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18105  * Up to three html elements can be associated with a DragDrop instance:
18106  * <ul>
18107  * <li>linked element: the element that is passed into the constructor.
18108  * This is the element which defines the boundaries for interaction with
18109  * other DragDrop objects.</li>
18110  * <li>handle element(s): The drag operation only occurs if the element that
18111  * was clicked matches a handle element.  By default this is the linked
18112  * element, but there are times that you will want only a portion of the
18113  * linked element to initiate the drag operation, and the setHandleElId()
18114  * method provides a way to define this.</li>
18115  * <li>drag element: this represents the element that would be moved along
18116  * with the cursor during a drag operation.  By default, this is the linked
18117  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18118  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18119  * </li>
18120  * </ul>
18121  * This class should not be instantiated until the onload event to ensure that
18122  * the associated elements are available.
18123  * The following would define a DragDrop obj that would interact with any
18124  * other DragDrop obj in the "group1" group:
18125  * <pre>
18126  *  dd = new Roo.dd.DragDrop("div1", "group1");
18127  * </pre>
18128  * Since none of the event handlers have been implemented, nothing would
18129  * actually happen if you were to run the code above.  Normally you would
18130  * override this class or one of the default implementations, but you can
18131  * also override the methods you want on an instance of the class...
18132  * <pre>
18133  *  dd.onDragDrop = function(e, id) {
18134  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18135  *  }
18136  * </pre>
18137  * @constructor
18138  * @param {String} id of the element that is linked to this instance
18139  * @param {String} sGroup the group of related DragDrop objects
18140  * @param {object} config an object containing configurable attributes
18141  *                Valid properties for DragDrop:
18142  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18143  */
18144 Roo.dd.DragDrop = function(id, sGroup, config) {
18145     if (id) {
18146         this.init(id, sGroup, config);
18147     }
18148     
18149 };
18150
18151 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18152
18153     /**
18154      * The id of the element associated with this object.  This is what we
18155      * refer to as the "linked element" because the size and position of
18156      * this element is used to determine when the drag and drop objects have
18157      * interacted.
18158      * @property id
18159      * @type String
18160      */
18161     id: null,
18162
18163     /**
18164      * Configuration attributes passed into the constructor
18165      * @property config
18166      * @type object
18167      */
18168     config: null,
18169
18170     /**
18171      * The id of the element that will be dragged.  By default this is same
18172      * as the linked element , but could be changed to another element. Ex:
18173      * Roo.dd.DDProxy
18174      * @property dragElId
18175      * @type String
18176      * @private
18177      */
18178     dragElId: null,
18179
18180     /**
18181      * the id of the element that initiates the drag operation.  By default
18182      * this is the linked element, but could be changed to be a child of this
18183      * element.  This lets us do things like only starting the drag when the
18184      * header element within the linked html element is clicked.
18185      * @property handleElId
18186      * @type String
18187      * @private
18188      */
18189     handleElId: null,
18190
18191     /**
18192      * An associative array of HTML tags that will be ignored if clicked.
18193      * @property invalidHandleTypes
18194      * @type {string: string}
18195      */
18196     invalidHandleTypes: null,
18197
18198     /**
18199      * An associative array of ids for elements that will be ignored if clicked
18200      * @property invalidHandleIds
18201      * @type {string: string}
18202      */
18203     invalidHandleIds: null,
18204
18205     /**
18206      * An indexted array of css class names for elements that will be ignored
18207      * if clicked.
18208      * @property invalidHandleClasses
18209      * @type string[]
18210      */
18211     invalidHandleClasses: null,
18212
18213     /**
18214      * The linked element's absolute X position at the time the drag was
18215      * started
18216      * @property startPageX
18217      * @type int
18218      * @private
18219      */
18220     startPageX: 0,
18221
18222     /**
18223      * The linked element's absolute X position at the time the drag was
18224      * started
18225      * @property startPageY
18226      * @type int
18227      * @private
18228      */
18229     startPageY: 0,
18230
18231     /**
18232      * The group defines a logical collection of DragDrop objects that are
18233      * related.  Instances only get events when interacting with other
18234      * DragDrop object in the same group.  This lets us define multiple
18235      * groups using a single DragDrop subclass if we want.
18236      * @property groups
18237      * @type {string: string}
18238      */
18239     groups: null,
18240
18241     /**
18242      * Individual drag/drop instances can be locked.  This will prevent
18243      * onmousedown start drag.
18244      * @property locked
18245      * @type boolean
18246      * @private
18247      */
18248     locked: false,
18249
18250     /**
18251      * Lock this instance
18252      * @method lock
18253      */
18254     lock: function() { this.locked = true; },
18255
18256     /**
18257      * Unlock this instace
18258      * @method unlock
18259      */
18260     unlock: function() { this.locked = false; },
18261
18262     /**
18263      * By default, all insances can be a drop target.  This can be disabled by
18264      * setting isTarget to false.
18265      * @method isTarget
18266      * @type boolean
18267      */
18268     isTarget: true,
18269
18270     /**
18271      * The padding configured for this drag and drop object for calculating
18272      * the drop zone intersection with this object.
18273      * @method padding
18274      * @type int[]
18275      */
18276     padding: null,
18277
18278     /**
18279      * Cached reference to the linked element
18280      * @property _domRef
18281      * @private
18282      */
18283     _domRef: null,
18284
18285     /**
18286      * Internal typeof flag
18287      * @property __ygDragDrop
18288      * @private
18289      */
18290     __ygDragDrop: true,
18291
18292     /**
18293      * Set to true when horizontal contraints are applied
18294      * @property constrainX
18295      * @type boolean
18296      * @private
18297      */
18298     constrainX: false,
18299
18300     /**
18301      * Set to true when vertical contraints are applied
18302      * @property constrainY
18303      * @type boolean
18304      * @private
18305      */
18306     constrainY: false,
18307
18308     /**
18309      * The left constraint
18310      * @property minX
18311      * @type int
18312      * @private
18313      */
18314     minX: 0,
18315
18316     /**
18317      * The right constraint
18318      * @property maxX
18319      * @type int
18320      * @private
18321      */
18322     maxX: 0,
18323
18324     /**
18325      * The up constraint
18326      * @property minY
18327      * @type int
18328      * @type int
18329      * @private
18330      */
18331     minY: 0,
18332
18333     /**
18334      * The down constraint
18335      * @property maxY
18336      * @type int
18337      * @private
18338      */
18339     maxY: 0,
18340
18341     /**
18342      * Maintain offsets when we resetconstraints.  Set to true when you want
18343      * the position of the element relative to its parent to stay the same
18344      * when the page changes
18345      *
18346      * @property maintainOffset
18347      * @type boolean
18348      */
18349     maintainOffset: false,
18350
18351     /**
18352      * Array of pixel locations the element will snap to if we specified a
18353      * horizontal graduation/interval.  This array is generated automatically
18354      * when you define a tick interval.
18355      * @property xTicks
18356      * @type int[]
18357      */
18358     xTicks: null,
18359
18360     /**
18361      * Array of pixel locations the element will snap to if we specified a
18362      * vertical graduation/interval.  This array is generated automatically
18363      * when you define a tick interval.
18364      * @property yTicks
18365      * @type int[]
18366      */
18367     yTicks: null,
18368
18369     /**
18370      * By default the drag and drop instance will only respond to the primary
18371      * button click (left button for a right-handed mouse).  Set to true to
18372      * allow drag and drop to start with any mouse click that is propogated
18373      * by the browser
18374      * @property primaryButtonOnly
18375      * @type boolean
18376      */
18377     primaryButtonOnly: true,
18378
18379     /**
18380      * The availabe property is false until the linked dom element is accessible.
18381      * @property available
18382      * @type boolean
18383      */
18384     available: false,
18385
18386     /**
18387      * By default, drags can only be initiated if the mousedown occurs in the
18388      * region the linked element is.  This is done in part to work around a
18389      * bug in some browsers that mis-report the mousedown if the previous
18390      * mouseup happened outside of the window.  This property is set to true
18391      * if outer handles are defined.
18392      *
18393      * @property hasOuterHandles
18394      * @type boolean
18395      * @default false
18396      */
18397     hasOuterHandles: false,
18398
18399     /**
18400      * Code that executes immediately before the startDrag event
18401      * @method b4StartDrag
18402      * @private
18403      */
18404     b4StartDrag: function(x, y) { },
18405
18406     /**
18407      * Abstract method called after a drag/drop object is clicked
18408      * and the drag or mousedown time thresholds have beeen met.
18409      * @method startDrag
18410      * @param {int} X click location
18411      * @param {int} Y click location
18412      */
18413     startDrag: function(x, y) { /* override this */ },
18414
18415     /**
18416      * Code that executes immediately before the onDrag event
18417      * @method b4Drag
18418      * @private
18419      */
18420     b4Drag: function(e) { },
18421
18422     /**
18423      * Abstract method called during the onMouseMove event while dragging an
18424      * object.
18425      * @method onDrag
18426      * @param {Event} e the mousemove event
18427      */
18428     onDrag: function(e) { /* override this */ },
18429
18430     /**
18431      * Abstract method called when this element fist begins hovering over
18432      * another DragDrop obj
18433      * @method onDragEnter
18434      * @param {Event} e the mousemove event
18435      * @param {String|DragDrop[]} id In POINT mode, the element
18436      * id this is hovering over.  In INTERSECT mode, an array of one or more
18437      * dragdrop items being hovered over.
18438      */
18439     onDragEnter: function(e, id) { /* override this */ },
18440
18441     /**
18442      * Code that executes immediately before the onDragOver event
18443      * @method b4DragOver
18444      * @private
18445      */
18446     b4DragOver: function(e) { },
18447
18448     /**
18449      * Abstract method called when this element is hovering over another
18450      * DragDrop obj
18451      * @method onDragOver
18452      * @param {Event} e the mousemove event
18453      * @param {String|DragDrop[]} id In POINT mode, the element
18454      * id this is hovering over.  In INTERSECT mode, an array of dd items
18455      * being hovered over.
18456      */
18457     onDragOver: function(e, id) { /* override this */ },
18458
18459     /**
18460      * Code that executes immediately before the onDragOut event
18461      * @method b4DragOut
18462      * @private
18463      */
18464     b4DragOut: function(e) { },
18465
18466     /**
18467      * Abstract method called when we are no longer hovering over an element
18468      * @method onDragOut
18469      * @param {Event} e the mousemove event
18470      * @param {String|DragDrop[]} id In POINT mode, the element
18471      * id this was hovering over.  In INTERSECT mode, an array of dd items
18472      * that the mouse is no longer over.
18473      */
18474     onDragOut: function(e, id) { /* override this */ },
18475
18476     /**
18477      * Code that executes immediately before the onDragDrop event
18478      * @method b4DragDrop
18479      * @private
18480      */
18481     b4DragDrop: function(e) { },
18482
18483     /**
18484      * Abstract method called when this item is dropped on another DragDrop
18485      * obj
18486      * @method onDragDrop
18487      * @param {Event} e the mouseup event
18488      * @param {String|DragDrop[]} id In POINT mode, the element
18489      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18490      * was dropped on.
18491      */
18492     onDragDrop: function(e, id) { /* override this */ },
18493
18494     /**
18495      * Abstract method called when this item is dropped on an area with no
18496      * drop target
18497      * @method onInvalidDrop
18498      * @param {Event} e the mouseup event
18499      */
18500     onInvalidDrop: function(e) { /* override this */ },
18501
18502     /**
18503      * Code that executes immediately before the endDrag event
18504      * @method b4EndDrag
18505      * @private
18506      */
18507     b4EndDrag: function(e) { },
18508
18509     /**
18510      * Fired when we are done dragging the object
18511      * @method endDrag
18512      * @param {Event} e the mouseup event
18513      */
18514     endDrag: function(e) { /* override this */ },
18515
18516     /**
18517      * Code executed immediately before the onMouseDown event
18518      * @method b4MouseDown
18519      * @param {Event} e the mousedown event
18520      * @private
18521      */
18522     b4MouseDown: function(e) {  },
18523
18524     /**
18525      * Event handler that fires when a drag/drop obj gets a mousedown
18526      * @method onMouseDown
18527      * @param {Event} e the mousedown event
18528      */
18529     onMouseDown: function(e) { /* override this */ },
18530
18531     /**
18532      * Event handler that fires when a drag/drop obj gets a mouseup
18533      * @method onMouseUp
18534      * @param {Event} e the mouseup event
18535      */
18536     onMouseUp: function(e) { /* override this */ },
18537
18538     /**
18539      * Override the onAvailable method to do what is needed after the initial
18540      * position was determined.
18541      * @method onAvailable
18542      */
18543     onAvailable: function () {
18544     },
18545
18546     /*
18547      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18548      * @type Object
18549      */
18550     defaultPadding : {left:0, right:0, top:0, bottom:0},
18551
18552     /*
18553      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18554  *
18555  * Usage:
18556  <pre><code>
18557  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18558                 { dragElId: "existingProxyDiv" });
18559  dd.startDrag = function(){
18560      this.constrainTo("parent-id");
18561  };
18562  </code></pre>
18563  * Or you can initalize it using the {@link Roo.Element} object:
18564  <pre><code>
18565  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18566      startDrag : function(){
18567          this.constrainTo("parent-id");
18568      }
18569  });
18570  </code></pre>
18571      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18572      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18573      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18574      * an object containing the sides to pad. For example: {right:10, bottom:10}
18575      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18576      */
18577     constrainTo : function(constrainTo, pad, inContent){
18578         if(typeof pad == "number"){
18579             pad = {left: pad, right:pad, top:pad, bottom:pad};
18580         }
18581         pad = pad || this.defaultPadding;
18582         var b = Roo.get(this.getEl()).getBox();
18583         var ce = Roo.get(constrainTo);
18584         var s = ce.getScroll();
18585         var c, cd = ce.dom;
18586         if(cd == document.body){
18587             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18588         }else{
18589             xy = ce.getXY();
18590             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18591         }
18592
18593
18594         var topSpace = b.y - c.y;
18595         var leftSpace = b.x - c.x;
18596
18597         this.resetConstraints();
18598         this.setXConstraint(leftSpace - (pad.left||0), // left
18599                 c.width - leftSpace - b.width - (pad.right||0) //right
18600         );
18601         this.setYConstraint(topSpace - (pad.top||0), //top
18602                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18603         );
18604     },
18605
18606     /**
18607      * Returns a reference to the linked element
18608      * @method getEl
18609      * @return {HTMLElement} the html element
18610      */
18611     getEl: function() {
18612         if (!this._domRef) {
18613             this._domRef = Roo.getDom(this.id);
18614         }
18615
18616         return this._domRef;
18617     },
18618
18619     /**
18620      * Returns a reference to the actual element to drag.  By default this is
18621      * the same as the html element, but it can be assigned to another
18622      * element. An example of this can be found in Roo.dd.DDProxy
18623      * @method getDragEl
18624      * @return {HTMLElement} the html element
18625      */
18626     getDragEl: function() {
18627         return Roo.getDom(this.dragElId);
18628     },
18629
18630     /**
18631      * Sets up the DragDrop object.  Must be called in the constructor of any
18632      * Roo.dd.DragDrop subclass
18633      * @method init
18634      * @param id the id of the linked element
18635      * @param {String} sGroup the group of related items
18636      * @param {object} config configuration attributes
18637      */
18638     init: function(id, sGroup, config) {
18639         this.initTarget(id, sGroup, config);
18640         if (!Roo.isTouch) {
18641             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18642         }
18643         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18644         // Event.on(this.id, "selectstart", Event.preventDefault);
18645     },
18646
18647     /**
18648      * Initializes Targeting functionality only... the object does not
18649      * get a mousedown handler.
18650      * @method initTarget
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     initTarget: function(id, sGroup, config) {
18656
18657         // configuration attributes
18658         this.config = config || {};
18659
18660         // create a local reference to the drag and drop manager
18661         this.DDM = Roo.dd.DDM;
18662         // initialize the groups array
18663         this.groups = {};
18664
18665         // assume that we have an element reference instead of an id if the
18666         // parameter is not a string
18667         if (typeof id !== "string") {
18668             id = Roo.id(id);
18669         }
18670
18671         // set the id
18672         this.id = id;
18673
18674         // add to an interaction group
18675         this.addToGroup((sGroup) ? sGroup : "default");
18676
18677         // We don't want to register this as the handle with the manager
18678         // so we just set the id rather than calling the setter.
18679         this.handleElId = id;
18680
18681         // the linked element is the element that gets dragged by default
18682         this.setDragElId(id);
18683
18684         // by default, clicked anchors will not start drag operations.
18685         this.invalidHandleTypes = { A: "A" };
18686         this.invalidHandleIds = {};
18687         this.invalidHandleClasses = [];
18688
18689         this.applyConfig();
18690
18691         this.handleOnAvailable();
18692     },
18693
18694     /**
18695      * Applies the configuration parameters that were passed into the constructor.
18696      * This is supposed to happen at each level through the inheritance chain.  So
18697      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18698      * DragDrop in order to get all of the parameters that are available in
18699      * each object.
18700      * @method applyConfig
18701      */
18702     applyConfig: function() {
18703
18704         // configurable properties:
18705         //    padding, isTarget, maintainOffset, primaryButtonOnly
18706         this.padding           = this.config.padding || [0, 0, 0, 0];
18707         this.isTarget          = (this.config.isTarget !== false);
18708         this.maintainOffset    = (this.config.maintainOffset);
18709         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18710
18711     },
18712
18713     /**
18714      * Executed when the linked element is available
18715      * @method handleOnAvailable
18716      * @private
18717      */
18718     handleOnAvailable: function() {
18719         this.available = true;
18720         this.resetConstraints();
18721         this.onAvailable();
18722     },
18723
18724      /**
18725      * Configures the padding for the target zone in px.  Effectively expands
18726      * (or reduces) the virtual object size for targeting calculations.
18727      * Supports css-style shorthand; if only one parameter is passed, all sides
18728      * will have that padding, and if only two are passed, the top and bottom
18729      * will have the first param, the left and right the second.
18730      * @method setPadding
18731      * @param {int} iTop    Top pad
18732      * @param {int} iRight  Right pad
18733      * @param {int} iBot    Bot pad
18734      * @param {int} iLeft   Left pad
18735      */
18736     setPadding: function(iTop, iRight, iBot, iLeft) {
18737         // this.padding = [iLeft, iRight, iTop, iBot];
18738         if (!iRight && 0 !== iRight) {
18739             this.padding = [iTop, iTop, iTop, iTop];
18740         } else if (!iBot && 0 !== iBot) {
18741             this.padding = [iTop, iRight, iTop, iRight];
18742         } else {
18743             this.padding = [iTop, iRight, iBot, iLeft];
18744         }
18745     },
18746
18747     /**
18748      * Stores the initial placement of the linked element.
18749      * @method setInitialPosition
18750      * @param {int} diffX   the X offset, default 0
18751      * @param {int} diffY   the Y offset, default 0
18752      */
18753     setInitPosition: function(diffX, diffY) {
18754         var el = this.getEl();
18755
18756         if (!this.DDM.verifyEl(el)) {
18757             return;
18758         }
18759
18760         var dx = diffX || 0;
18761         var dy = diffY || 0;
18762
18763         var p = Dom.getXY( el );
18764
18765         this.initPageX = p[0] - dx;
18766         this.initPageY = p[1] - dy;
18767
18768         this.lastPageX = p[0];
18769         this.lastPageY = p[1];
18770
18771
18772         this.setStartPosition(p);
18773     },
18774
18775     /**
18776      * Sets the start position of the element.  This is set when the obj
18777      * is initialized, the reset when a drag is started.
18778      * @method setStartPosition
18779      * @param pos current position (from previous lookup)
18780      * @private
18781      */
18782     setStartPosition: function(pos) {
18783         var p = pos || Dom.getXY( this.getEl() );
18784         this.deltaSetXY = null;
18785
18786         this.startPageX = p[0];
18787         this.startPageY = p[1];
18788     },
18789
18790     /**
18791      * Add this instance to a group of related drag/drop objects.  All
18792      * instances belong to at least one group, and can belong to as many
18793      * groups as needed.
18794      * @method addToGroup
18795      * @param sGroup {string} the name of the group
18796      */
18797     addToGroup: function(sGroup) {
18798         this.groups[sGroup] = true;
18799         this.DDM.regDragDrop(this, sGroup);
18800     },
18801
18802     /**
18803      * Remove's this instance from the supplied interaction group
18804      * @method removeFromGroup
18805      * @param {string}  sGroup  The group to drop
18806      */
18807     removeFromGroup: function(sGroup) {
18808         if (this.groups[sGroup]) {
18809             delete this.groups[sGroup];
18810         }
18811
18812         this.DDM.removeDDFromGroup(this, sGroup);
18813     },
18814
18815     /**
18816      * Allows you to specify that an element other than the linked element
18817      * will be moved with the cursor during a drag
18818      * @method setDragElId
18819      * @param id {string} the id of the element that will be used to initiate the drag
18820      */
18821     setDragElId: function(id) {
18822         this.dragElId = id;
18823     },
18824
18825     /**
18826      * Allows you to specify a child of the linked element that should be
18827      * used to initiate the drag operation.  An example of this would be if
18828      * you have a content div with text and links.  Clicking anywhere in the
18829      * content area would normally start the drag operation.  Use this method
18830      * to specify that an element inside of the content div is the element
18831      * that starts the drag operation.
18832      * @method setHandleElId
18833      * @param id {string} the id of the element that will be used to
18834      * initiate the drag.
18835      */
18836     setHandleElId: function(id) {
18837         if (typeof id !== "string") {
18838             id = Roo.id(id);
18839         }
18840         this.handleElId = id;
18841         this.DDM.regHandle(this.id, id);
18842     },
18843
18844     /**
18845      * Allows you to set an element outside of the linked element as a drag
18846      * handle
18847      * @method setOuterHandleElId
18848      * @param id the id of the element that will be used to initiate the drag
18849      */
18850     setOuterHandleElId: function(id) {
18851         if (typeof id !== "string") {
18852             id = Roo.id(id);
18853         }
18854         Event.on(id, "mousedown",
18855                 this.handleMouseDown, this);
18856         this.setHandleElId(id);
18857
18858         this.hasOuterHandles = true;
18859     },
18860
18861     /**
18862      * Remove all drag and drop hooks for this element
18863      * @method unreg
18864      */
18865     unreg: function() {
18866         Event.un(this.id, "mousedown",
18867                 this.handleMouseDown);
18868         Event.un(this.id, "touchstart",
18869                 this.handleMouseDown);
18870         this._domRef = null;
18871         this.DDM._remove(this);
18872     },
18873
18874     destroy : function(){
18875         this.unreg();
18876     },
18877
18878     /**
18879      * Returns true if this instance is locked, or the drag drop mgr is locked
18880      * (meaning that all drag/drop is disabled on the page.)
18881      * @method isLocked
18882      * @return {boolean} true if this obj or all drag/drop is locked, else
18883      * false
18884      */
18885     isLocked: function() {
18886         return (this.DDM.isLocked() || this.locked);
18887     },
18888
18889     /**
18890      * Fired when this object is clicked
18891      * @method handleMouseDown
18892      * @param {Event} e
18893      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18894      * @private
18895      */
18896     handleMouseDown: function(e, oDD){
18897      
18898         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18899             //Roo.log('not touch/ button !=0');
18900             return;
18901         }
18902         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18903             return; // double touch..
18904         }
18905         
18906
18907         if (this.isLocked()) {
18908             //Roo.log('locked');
18909             return;
18910         }
18911
18912         this.DDM.refreshCache(this.groups);
18913 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18914         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18915         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18916             //Roo.log('no outer handes or not over target');
18917                 // do nothing.
18918         } else {
18919 //            Roo.log('check validator');
18920             if (this.clickValidator(e)) {
18921 //                Roo.log('validate success');
18922                 // set the initial element position
18923                 this.setStartPosition();
18924
18925
18926                 this.b4MouseDown(e);
18927                 this.onMouseDown(e);
18928
18929                 this.DDM.handleMouseDown(e, this);
18930
18931                 this.DDM.stopEvent(e);
18932             } else {
18933
18934
18935             }
18936         }
18937     },
18938
18939     clickValidator: function(e) {
18940         var target = e.getTarget();
18941         return ( this.isValidHandleChild(target) &&
18942                     (this.id == this.handleElId ||
18943                         this.DDM.handleWasClicked(target, this.id)) );
18944     },
18945
18946     /**
18947      * Allows you to specify a tag name that should not start a drag operation
18948      * when clicked.  This is designed to facilitate embedding links within a
18949      * drag handle that do something other than start the drag.
18950      * @method addInvalidHandleType
18951      * @param {string} tagName the type of element to exclude
18952      */
18953     addInvalidHandleType: function(tagName) {
18954         var type = tagName.toUpperCase();
18955         this.invalidHandleTypes[type] = type;
18956     },
18957
18958     /**
18959      * Lets you to specify an element id for a child of a drag handle
18960      * that should not initiate a drag
18961      * @method addInvalidHandleId
18962      * @param {string} id the element id of the element you wish to ignore
18963      */
18964     addInvalidHandleId: function(id) {
18965         if (typeof id !== "string") {
18966             id = Roo.id(id);
18967         }
18968         this.invalidHandleIds[id] = id;
18969     },
18970
18971     /**
18972      * Lets you specify a css class of elements that will not initiate a drag
18973      * @method addInvalidHandleClass
18974      * @param {string} cssClass the class of the elements you wish to ignore
18975      */
18976     addInvalidHandleClass: function(cssClass) {
18977         this.invalidHandleClasses.push(cssClass);
18978     },
18979
18980     /**
18981      * Unsets an excluded tag name set by addInvalidHandleType
18982      * @method removeInvalidHandleType
18983      * @param {string} tagName the type of element to unexclude
18984      */
18985     removeInvalidHandleType: function(tagName) {
18986         var type = tagName.toUpperCase();
18987         // this.invalidHandleTypes[type] = null;
18988         delete this.invalidHandleTypes[type];
18989     },
18990
18991     /**
18992      * Unsets an invalid handle id
18993      * @method removeInvalidHandleId
18994      * @param {string} id the id of the element to re-enable
18995      */
18996     removeInvalidHandleId: function(id) {
18997         if (typeof id !== "string") {
18998             id = Roo.id(id);
18999         }
19000         delete this.invalidHandleIds[id];
19001     },
19002
19003     /**
19004      * Unsets an invalid css class
19005      * @method removeInvalidHandleClass
19006      * @param {string} cssClass the class of the element(s) you wish to
19007      * re-enable
19008      */
19009     removeInvalidHandleClass: function(cssClass) {
19010         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19011             if (this.invalidHandleClasses[i] == cssClass) {
19012                 delete this.invalidHandleClasses[i];
19013             }
19014         }
19015     },
19016
19017     /**
19018      * Checks the tag exclusion list to see if this click should be ignored
19019      * @method isValidHandleChild
19020      * @param {HTMLElement} node the HTMLElement to evaluate
19021      * @return {boolean} true if this is a valid tag type, false if not
19022      */
19023     isValidHandleChild: function(node) {
19024
19025         var valid = true;
19026         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19027         var nodeName;
19028         try {
19029             nodeName = node.nodeName.toUpperCase();
19030         } catch(e) {
19031             nodeName = node.nodeName;
19032         }
19033         valid = valid && !this.invalidHandleTypes[nodeName];
19034         valid = valid && !this.invalidHandleIds[node.id];
19035
19036         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19037             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19038         }
19039
19040
19041         return valid;
19042
19043     },
19044
19045     /**
19046      * Create the array of horizontal tick marks if an interval was specified
19047      * in setXConstraint().
19048      * @method setXTicks
19049      * @private
19050      */
19051     setXTicks: function(iStartX, iTickSize) {
19052         this.xTicks = [];
19053         this.xTickSize = iTickSize;
19054
19055         var tickMap = {};
19056
19057         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19058             if (!tickMap[i]) {
19059                 this.xTicks[this.xTicks.length] = i;
19060                 tickMap[i] = true;
19061             }
19062         }
19063
19064         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19065             if (!tickMap[i]) {
19066                 this.xTicks[this.xTicks.length] = i;
19067                 tickMap[i] = true;
19068             }
19069         }
19070
19071         this.xTicks.sort(this.DDM.numericSort) ;
19072     },
19073
19074     /**
19075      * Create the array of vertical tick marks if an interval was specified in
19076      * setYConstraint().
19077      * @method setYTicks
19078      * @private
19079      */
19080     setYTicks: function(iStartY, iTickSize) {
19081         this.yTicks = [];
19082         this.yTickSize = iTickSize;
19083
19084         var tickMap = {};
19085
19086         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19087             if (!tickMap[i]) {
19088                 this.yTicks[this.yTicks.length] = i;
19089                 tickMap[i] = true;
19090             }
19091         }
19092
19093         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19094             if (!tickMap[i]) {
19095                 this.yTicks[this.yTicks.length] = i;
19096                 tickMap[i] = true;
19097             }
19098         }
19099
19100         this.yTicks.sort(this.DDM.numericSort) ;
19101     },
19102
19103     /**
19104      * By default, the element can be dragged any place on the screen.  Use
19105      * this method to limit the horizontal travel of the element.  Pass in
19106      * 0,0 for the parameters if you want to lock the drag to the y axis.
19107      * @method setXConstraint
19108      * @param {int} iLeft the number of pixels the element can move to the left
19109      * @param {int} iRight the number of pixels the element can move to the
19110      * right
19111      * @param {int} iTickSize optional parameter for specifying that the
19112      * element
19113      * should move iTickSize pixels at a time.
19114      */
19115     setXConstraint: function(iLeft, iRight, iTickSize) {
19116         this.leftConstraint = iLeft;
19117         this.rightConstraint = iRight;
19118
19119         this.minX = this.initPageX - iLeft;
19120         this.maxX = this.initPageX + iRight;
19121         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19122
19123         this.constrainX = true;
19124     },
19125
19126     /**
19127      * Clears any constraints applied to this instance.  Also clears ticks
19128      * since they can't exist independent of a constraint at this time.
19129      * @method clearConstraints
19130      */
19131     clearConstraints: function() {
19132         this.constrainX = false;
19133         this.constrainY = false;
19134         this.clearTicks();
19135     },
19136
19137     /**
19138      * Clears any tick interval defined for this instance
19139      * @method clearTicks
19140      */
19141     clearTicks: function() {
19142         this.xTicks = null;
19143         this.yTicks = null;
19144         this.xTickSize = 0;
19145         this.yTickSize = 0;
19146     },
19147
19148     /**
19149      * By default, the element can be dragged any place on the screen.  Set
19150      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19151      * parameters if you want to lock the drag to the x axis.
19152      * @method setYConstraint
19153      * @param {int} iUp the number of pixels the element can move up
19154      * @param {int} iDown the number of pixels the element can move down
19155      * @param {int} iTickSize optional parameter for specifying that the
19156      * element should move iTickSize pixels at a time.
19157      */
19158     setYConstraint: function(iUp, iDown, iTickSize) {
19159         this.topConstraint = iUp;
19160         this.bottomConstraint = iDown;
19161
19162         this.minY = this.initPageY - iUp;
19163         this.maxY = this.initPageY + iDown;
19164         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19165
19166         this.constrainY = true;
19167
19168     },
19169
19170     /**
19171      * resetConstraints must be called if you manually reposition a dd element.
19172      * @method resetConstraints
19173      * @param {boolean} maintainOffset
19174      */
19175     resetConstraints: function() {
19176
19177
19178         // Maintain offsets if necessary
19179         if (this.initPageX || this.initPageX === 0) {
19180             // figure out how much this thing has moved
19181             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19182             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19183
19184             this.setInitPosition(dx, dy);
19185
19186         // This is the first time we have detected the element's position
19187         } else {
19188             this.setInitPosition();
19189         }
19190
19191         if (this.constrainX) {
19192             this.setXConstraint( this.leftConstraint,
19193                                  this.rightConstraint,
19194                                  this.xTickSize        );
19195         }
19196
19197         if (this.constrainY) {
19198             this.setYConstraint( this.topConstraint,
19199                                  this.bottomConstraint,
19200                                  this.yTickSize         );
19201         }
19202     },
19203
19204     /**
19205      * Normally the drag element is moved pixel by pixel, but we can specify
19206      * that it move a number of pixels at a time.  This method resolves the
19207      * location when we have it set up like this.
19208      * @method getTick
19209      * @param {int} val where we want to place the object
19210      * @param {int[]} tickArray sorted array of valid points
19211      * @return {int} the closest tick
19212      * @private
19213      */
19214     getTick: function(val, tickArray) {
19215
19216         if (!tickArray) {
19217             // If tick interval is not defined, it is effectively 1 pixel,
19218             // so we return the value passed to us.
19219             return val;
19220         } else if (tickArray[0] >= val) {
19221             // The value is lower than the first tick, so we return the first
19222             // tick.
19223             return tickArray[0];
19224         } else {
19225             for (var i=0, len=tickArray.length; i<len; ++i) {
19226                 var next = i + 1;
19227                 if (tickArray[next] && tickArray[next] >= val) {
19228                     var diff1 = val - tickArray[i];
19229                     var diff2 = tickArray[next] - val;
19230                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19231                 }
19232             }
19233
19234             // The value is larger than the last tick, so we return the last
19235             // tick.
19236             return tickArray[tickArray.length - 1];
19237         }
19238     },
19239
19240     /**
19241      * toString method
19242      * @method toString
19243      * @return {string} string representation of the dd obj
19244      */
19245     toString: function() {
19246         return ("DragDrop " + this.id);
19247     }
19248
19249 });
19250
19251 })();
19252 /*
19253  * Based on:
19254  * Ext JS Library 1.1.1
19255  * Copyright(c) 2006-2007, Ext JS, LLC.
19256  *
19257  * Originally Released Under LGPL - original licence link has changed is not relivant.
19258  *
19259  * Fork - LGPL
19260  * <script type="text/javascript">
19261  */
19262
19263
19264 /**
19265  * The drag and drop utility provides a framework for building drag and drop
19266  * applications.  In addition to enabling drag and drop for specific elements,
19267  * the drag and drop elements are tracked by the manager class, and the
19268  * interactions between the various elements are tracked during the drag and
19269  * the implementing code is notified about these important moments.
19270  */
19271
19272 // Only load the library once.  Rewriting the manager class would orphan
19273 // existing drag and drop instances.
19274 if (!Roo.dd.DragDropMgr) {
19275
19276 /**
19277  * @class Roo.dd.DragDropMgr
19278  * DragDropMgr is a singleton that tracks the element interaction for
19279  * all DragDrop items in the window.  Generally, you will not call
19280  * this class directly, but it does have helper methods that could
19281  * be useful in your DragDrop implementations.
19282  * @singleton
19283  */
19284 Roo.dd.DragDropMgr = function() {
19285
19286     var Event = Roo.EventManager;
19287
19288     return {
19289
19290         /**
19291          * Two dimensional Array of registered DragDrop objects.  The first
19292          * dimension is the DragDrop item group, the second the DragDrop
19293          * object.
19294          * @property ids
19295          * @type {string: string}
19296          * @private
19297          * @static
19298          */
19299         ids: {},
19300
19301         /**
19302          * Array of element ids defined as drag handles.  Used to determine
19303          * if the element that generated the mousedown event is actually the
19304          * handle and not the html element itself.
19305          * @property handleIds
19306          * @type {string: string}
19307          * @private
19308          * @static
19309          */
19310         handleIds: {},
19311
19312         /**
19313          * the DragDrop object that is currently being dragged
19314          * @property dragCurrent
19315          * @type DragDrop
19316          * @private
19317          * @static
19318          **/
19319         dragCurrent: null,
19320
19321         /**
19322          * the DragDrop object(s) that are being hovered over
19323          * @property dragOvers
19324          * @type Array
19325          * @private
19326          * @static
19327          */
19328         dragOvers: {},
19329
19330         /**
19331          * the X distance between the cursor and the object being dragged
19332          * @property deltaX
19333          * @type int
19334          * @private
19335          * @static
19336          */
19337         deltaX: 0,
19338
19339         /**
19340          * the Y distance between the cursor and the object being dragged
19341          * @property deltaY
19342          * @type int
19343          * @private
19344          * @static
19345          */
19346         deltaY: 0,
19347
19348         /**
19349          * Flag to determine if we should prevent the default behavior of the
19350          * events we define. By default this is true, but this can be set to
19351          * false if you need the default behavior (not recommended)
19352          * @property preventDefault
19353          * @type boolean
19354          * @static
19355          */
19356         preventDefault: true,
19357
19358         /**
19359          * Flag to determine if we should stop the propagation of the events
19360          * we generate. This is true by default but you may want to set it to
19361          * false if the html element contains other features that require the
19362          * mouse click.
19363          * @property stopPropagation
19364          * @type boolean
19365          * @static
19366          */
19367         stopPropagation: true,
19368
19369         /**
19370          * Internal flag that is set to true when drag and drop has been
19371          * intialized
19372          * @property initialized
19373          * @private
19374          * @static
19375          */
19376         initalized: false,
19377
19378         /**
19379          * All drag and drop can be disabled.
19380          * @property locked
19381          * @private
19382          * @static
19383          */
19384         locked: false,
19385
19386         /**
19387          * Called the first time an element is registered.
19388          * @method init
19389          * @private
19390          * @static
19391          */
19392         init: function() {
19393             this.initialized = true;
19394         },
19395
19396         /**
19397          * In point mode, drag and drop interaction is defined by the
19398          * location of the cursor during the drag/drop
19399          * @property POINT
19400          * @type int
19401          * @static
19402          */
19403         POINT: 0,
19404
19405         /**
19406          * In intersect mode, drag and drop interactio nis defined by the
19407          * overlap of two or more drag and drop objects.
19408          * @property INTERSECT
19409          * @type int
19410          * @static
19411          */
19412         INTERSECT: 1,
19413
19414         /**
19415          * The current drag and drop mode.  Default: POINT
19416          * @property mode
19417          * @type int
19418          * @static
19419          */
19420         mode: 0,
19421
19422         /**
19423          * Runs method on all drag and drop objects
19424          * @method _execOnAll
19425          * @private
19426          * @static
19427          */
19428         _execOnAll: function(sMethod, args) {
19429             for (var i in this.ids) {
19430                 for (var j in this.ids[i]) {
19431                     var oDD = this.ids[i][j];
19432                     if (! this.isTypeOfDD(oDD)) {
19433                         continue;
19434                     }
19435                     oDD[sMethod].apply(oDD, args);
19436                 }
19437             }
19438         },
19439
19440         /**
19441          * Drag and drop initialization.  Sets up the global event handlers
19442          * @method _onLoad
19443          * @private
19444          * @static
19445          */
19446         _onLoad: function() {
19447
19448             this.init();
19449
19450             if (!Roo.isTouch) {
19451                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19452                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19453             }
19454             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19455             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19456             
19457             Event.on(window,   "unload",    this._onUnload, this, true);
19458             Event.on(window,   "resize",    this._onResize, this, true);
19459             // Event.on(window,   "mouseout",    this._test);
19460
19461         },
19462
19463         /**
19464          * Reset constraints on all drag and drop objs
19465          * @method _onResize
19466          * @private
19467          * @static
19468          */
19469         _onResize: function(e) {
19470             this._execOnAll("resetConstraints", []);
19471         },
19472
19473         /**
19474          * Lock all drag and drop functionality
19475          * @method lock
19476          * @static
19477          */
19478         lock: function() { this.locked = true; },
19479
19480         /**
19481          * Unlock all drag and drop functionality
19482          * @method unlock
19483          * @static
19484          */
19485         unlock: function() { this.locked = false; },
19486
19487         /**
19488          * Is drag and drop locked?
19489          * @method isLocked
19490          * @return {boolean} True if drag and drop is locked, false otherwise.
19491          * @static
19492          */
19493         isLocked: function() { return this.locked; },
19494
19495         /**
19496          * Location cache that is set for all drag drop objects when a drag is
19497          * initiated, cleared when the drag is finished.
19498          * @property locationCache
19499          * @private
19500          * @static
19501          */
19502         locationCache: {},
19503
19504         /**
19505          * Set useCache to false if you want to force object the lookup of each
19506          * drag and drop linked element constantly during a drag.
19507          * @property useCache
19508          * @type boolean
19509          * @static
19510          */
19511         useCache: true,
19512
19513         /**
19514          * The number of pixels that the mouse needs to move after the
19515          * mousedown before the drag is initiated.  Default=3;
19516          * @property clickPixelThresh
19517          * @type int
19518          * @static
19519          */
19520         clickPixelThresh: 3,
19521
19522         /**
19523          * The number of milliseconds after the mousedown event to initiate the
19524          * drag if we don't get a mouseup event. Default=1000
19525          * @property clickTimeThresh
19526          * @type int
19527          * @static
19528          */
19529         clickTimeThresh: 350,
19530
19531         /**
19532          * Flag that indicates that either the drag pixel threshold or the
19533          * mousdown time threshold has been met
19534          * @property dragThreshMet
19535          * @type boolean
19536          * @private
19537          * @static
19538          */
19539         dragThreshMet: false,
19540
19541         /**
19542          * Timeout used for the click time threshold
19543          * @property clickTimeout
19544          * @type Object
19545          * @private
19546          * @static
19547          */
19548         clickTimeout: null,
19549
19550         /**
19551          * The X position of the mousedown event stored for later use when a
19552          * drag threshold is met.
19553          * @property startX
19554          * @type int
19555          * @private
19556          * @static
19557          */
19558         startX: 0,
19559
19560         /**
19561          * The Y position of the mousedown event stored for later use when a
19562          * drag threshold is met.
19563          * @property startY
19564          * @type int
19565          * @private
19566          * @static
19567          */
19568         startY: 0,
19569
19570         /**
19571          * Each DragDrop instance must be registered with the DragDropMgr.
19572          * This is executed in DragDrop.init()
19573          * @method regDragDrop
19574          * @param {DragDrop} oDD the DragDrop object to register
19575          * @param {String} sGroup the name of the group this element belongs to
19576          * @static
19577          */
19578         regDragDrop: function(oDD, sGroup) {
19579             if (!this.initialized) { this.init(); }
19580
19581             if (!this.ids[sGroup]) {
19582                 this.ids[sGroup] = {};
19583             }
19584             this.ids[sGroup][oDD.id] = oDD;
19585         },
19586
19587         /**
19588          * Removes the supplied dd instance from the supplied group. Executed
19589          * by DragDrop.removeFromGroup, so don't call this function directly.
19590          * @method removeDDFromGroup
19591          * @private
19592          * @static
19593          */
19594         removeDDFromGroup: function(oDD, sGroup) {
19595             if (!this.ids[sGroup]) {
19596                 this.ids[sGroup] = {};
19597             }
19598
19599             var obj = this.ids[sGroup];
19600             if (obj && obj[oDD.id]) {
19601                 delete obj[oDD.id];
19602             }
19603         },
19604
19605         /**
19606          * Unregisters a drag and drop item.  This is executed in
19607          * DragDrop.unreg, use that method instead of calling this directly.
19608          * @method _remove
19609          * @private
19610          * @static
19611          */
19612         _remove: function(oDD) {
19613             for (var g in oDD.groups) {
19614                 if (g && this.ids[g][oDD.id]) {
19615                     delete this.ids[g][oDD.id];
19616                 }
19617             }
19618             delete this.handleIds[oDD.id];
19619         },
19620
19621         /**
19622          * Each DragDrop handle element must be registered.  This is done
19623          * automatically when executing DragDrop.setHandleElId()
19624          * @method regHandle
19625          * @param {String} sDDId the DragDrop id this element is a handle for
19626          * @param {String} sHandleId the id of the element that is the drag
19627          * handle
19628          * @static
19629          */
19630         regHandle: function(sDDId, sHandleId) {
19631             if (!this.handleIds[sDDId]) {
19632                 this.handleIds[sDDId] = {};
19633             }
19634             this.handleIds[sDDId][sHandleId] = sHandleId;
19635         },
19636
19637         /**
19638          * Utility function to determine if a given element has been
19639          * registered as a drag drop item.
19640          * @method isDragDrop
19641          * @param {String} id the element id to check
19642          * @return {boolean} true if this element is a DragDrop item,
19643          * false otherwise
19644          * @static
19645          */
19646         isDragDrop: function(id) {
19647             return ( this.getDDById(id) ) ? true : false;
19648         },
19649
19650         /**
19651          * Returns the drag and drop instances that are in all groups the
19652          * passed in instance belongs to.
19653          * @method getRelated
19654          * @param {DragDrop} p_oDD the obj to get related data for
19655          * @param {boolean} bTargetsOnly if true, only return targetable objs
19656          * @return {DragDrop[]} the related instances
19657          * @static
19658          */
19659         getRelated: function(p_oDD, bTargetsOnly) {
19660             var oDDs = [];
19661             for (var i in p_oDD.groups) {
19662                 for (j in this.ids[i]) {
19663                     var dd = this.ids[i][j];
19664                     if (! this.isTypeOfDD(dd)) {
19665                         continue;
19666                     }
19667                     if (!bTargetsOnly || dd.isTarget) {
19668                         oDDs[oDDs.length] = dd;
19669                     }
19670                 }
19671             }
19672
19673             return oDDs;
19674         },
19675
19676         /**
19677          * Returns true if the specified dd target is a legal target for
19678          * the specifice drag obj
19679          * @method isLegalTarget
19680          * @param {DragDrop} the drag obj
19681          * @param {DragDrop} the target
19682          * @return {boolean} true if the target is a legal target for the
19683          * dd obj
19684          * @static
19685          */
19686         isLegalTarget: function (oDD, oTargetDD) {
19687             var targets = this.getRelated(oDD, true);
19688             for (var i=0, len=targets.length;i<len;++i) {
19689                 if (targets[i].id == oTargetDD.id) {
19690                     return true;
19691                 }
19692             }
19693
19694             return false;
19695         },
19696
19697         /**
19698          * My goal is to be able to transparently determine if an object is
19699          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19700          * returns "object", oDD.constructor.toString() always returns
19701          * "DragDrop" and not the name of the subclass.  So for now it just
19702          * evaluates a well-known variable in DragDrop.
19703          * @method isTypeOfDD
19704          * @param {Object} the object to evaluate
19705          * @return {boolean} true if typeof oDD = DragDrop
19706          * @static
19707          */
19708         isTypeOfDD: function (oDD) {
19709             return (oDD && oDD.__ygDragDrop);
19710         },
19711
19712         /**
19713          * Utility function to determine if a given element has been
19714          * registered as a drag drop handle for the given Drag Drop object.
19715          * @method isHandle
19716          * @param {String} id the element id to check
19717          * @return {boolean} true if this element is a DragDrop handle, false
19718          * otherwise
19719          * @static
19720          */
19721         isHandle: function(sDDId, sHandleId) {
19722             return ( this.handleIds[sDDId] &&
19723                             this.handleIds[sDDId][sHandleId] );
19724         },
19725
19726         /**
19727          * Returns the DragDrop instance for a given id
19728          * @method getDDById
19729          * @param {String} id the id of the DragDrop object
19730          * @return {DragDrop} the drag drop object, null if it is not found
19731          * @static
19732          */
19733         getDDById: function(id) {
19734             for (var i in this.ids) {
19735                 if (this.ids[i][id]) {
19736                     return this.ids[i][id];
19737                 }
19738             }
19739             return null;
19740         },
19741
19742         /**
19743          * Fired after a registered DragDrop object gets the mousedown event.
19744          * Sets up the events required to track the object being dragged
19745          * @method handleMouseDown
19746          * @param {Event} e the event
19747          * @param oDD the DragDrop object being dragged
19748          * @private
19749          * @static
19750          */
19751         handleMouseDown: function(e, oDD) {
19752             if(Roo.QuickTips){
19753                 Roo.QuickTips.disable();
19754             }
19755             this.currentTarget = e.getTarget();
19756
19757             this.dragCurrent = oDD;
19758
19759             var el = oDD.getEl();
19760
19761             // track start position
19762             this.startX = e.getPageX();
19763             this.startY = e.getPageY();
19764
19765             this.deltaX = this.startX - el.offsetLeft;
19766             this.deltaY = this.startY - el.offsetTop;
19767
19768             this.dragThreshMet = false;
19769
19770             this.clickTimeout = setTimeout(
19771                     function() {
19772                         var DDM = Roo.dd.DDM;
19773                         DDM.startDrag(DDM.startX, DDM.startY);
19774                     },
19775                     this.clickTimeThresh );
19776         },
19777
19778         /**
19779          * Fired when either the drag pixel threshol or the mousedown hold
19780          * time threshold has been met.
19781          * @method startDrag
19782          * @param x {int} the X position of the original mousedown
19783          * @param y {int} the Y position of the original mousedown
19784          * @static
19785          */
19786         startDrag: function(x, y) {
19787             clearTimeout(this.clickTimeout);
19788             if (this.dragCurrent) {
19789                 this.dragCurrent.b4StartDrag(x, y);
19790                 this.dragCurrent.startDrag(x, y);
19791             }
19792             this.dragThreshMet = true;
19793         },
19794
19795         /**
19796          * Internal function to handle the mouseup event.  Will be invoked
19797          * from the context of the document.
19798          * @method handleMouseUp
19799          * @param {Event} e the event
19800          * @private
19801          * @static
19802          */
19803         handleMouseUp: function(e) {
19804
19805             if(Roo.QuickTips){
19806                 Roo.QuickTips.enable();
19807             }
19808             if (! this.dragCurrent) {
19809                 return;
19810             }
19811
19812             clearTimeout(this.clickTimeout);
19813
19814             if (this.dragThreshMet) {
19815                 this.fireEvents(e, true);
19816             } else {
19817             }
19818
19819             this.stopDrag(e);
19820
19821             this.stopEvent(e);
19822         },
19823
19824         /**
19825          * Utility to stop event propagation and event default, if these
19826          * features are turned on.
19827          * @method stopEvent
19828          * @param {Event} e the event as returned by this.getEvent()
19829          * @static
19830          */
19831         stopEvent: function(e){
19832             if(this.stopPropagation) {
19833                 e.stopPropagation();
19834             }
19835
19836             if (this.preventDefault) {
19837                 e.preventDefault();
19838             }
19839         },
19840
19841         /**
19842          * Internal function to clean up event handlers after the drag
19843          * operation is complete
19844          * @method stopDrag
19845          * @param {Event} e the event
19846          * @private
19847          * @static
19848          */
19849         stopDrag: function(e) {
19850             // Fire the drag end event for the item that was dragged
19851             if (this.dragCurrent) {
19852                 if (this.dragThreshMet) {
19853                     this.dragCurrent.b4EndDrag(e);
19854                     this.dragCurrent.endDrag(e);
19855                 }
19856
19857                 this.dragCurrent.onMouseUp(e);
19858             }
19859
19860             this.dragCurrent = null;
19861             this.dragOvers = {};
19862         },
19863
19864         /**
19865          * Internal function to handle the mousemove event.  Will be invoked
19866          * from the context of the html element.
19867          *
19868          * @TODO figure out what we can do about mouse events lost when the
19869          * user drags objects beyond the window boundary.  Currently we can
19870          * detect this in internet explorer by verifying that the mouse is
19871          * down during the mousemove event.  Firefox doesn't give us the
19872          * button state on the mousemove event.
19873          * @method handleMouseMove
19874          * @param {Event} e the event
19875          * @private
19876          * @static
19877          */
19878         handleMouseMove: function(e) {
19879             if (! this.dragCurrent) {
19880                 return true;
19881             }
19882
19883             // var button = e.which || e.button;
19884
19885             // check for IE mouseup outside of page boundary
19886             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19887                 this.stopEvent(e);
19888                 return this.handleMouseUp(e);
19889             }
19890
19891             if (!this.dragThreshMet) {
19892                 var diffX = Math.abs(this.startX - e.getPageX());
19893                 var diffY = Math.abs(this.startY - e.getPageY());
19894                 if (diffX > this.clickPixelThresh ||
19895                             diffY > this.clickPixelThresh) {
19896                     this.startDrag(this.startX, this.startY);
19897                 }
19898             }
19899
19900             if (this.dragThreshMet) {
19901                 this.dragCurrent.b4Drag(e);
19902                 this.dragCurrent.onDrag(e);
19903                 if(!this.dragCurrent.moveOnly){
19904                     this.fireEvents(e, false);
19905                 }
19906             }
19907
19908             this.stopEvent(e);
19909
19910             return true;
19911         },
19912
19913         /**
19914          * Iterates over all of the DragDrop elements to find ones we are
19915          * hovering over or dropping on
19916          * @method fireEvents
19917          * @param {Event} e the event
19918          * @param {boolean} isDrop is this a drop op or a mouseover op?
19919          * @private
19920          * @static
19921          */
19922         fireEvents: function(e, isDrop) {
19923             var dc = this.dragCurrent;
19924
19925             // If the user did the mouse up outside of the window, we could
19926             // get here even though we have ended the drag.
19927             if (!dc || dc.isLocked()) {
19928                 return;
19929             }
19930
19931             var pt = e.getPoint();
19932
19933             // cache the previous dragOver array
19934             var oldOvers = [];
19935
19936             var outEvts   = [];
19937             var overEvts  = [];
19938             var dropEvts  = [];
19939             var enterEvts = [];
19940
19941             // Check to see if the object(s) we were hovering over is no longer
19942             // being hovered over so we can fire the onDragOut event
19943             for (var i in this.dragOvers) {
19944
19945                 var ddo = this.dragOvers[i];
19946
19947                 if (! this.isTypeOfDD(ddo)) {
19948                     continue;
19949                 }
19950
19951                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19952                     outEvts.push( ddo );
19953                 }
19954
19955                 oldOvers[i] = true;
19956                 delete this.dragOvers[i];
19957             }
19958
19959             for (var sGroup in dc.groups) {
19960
19961                 if ("string" != typeof sGroup) {
19962                     continue;
19963                 }
19964
19965                 for (i in this.ids[sGroup]) {
19966                     var oDD = this.ids[sGroup][i];
19967                     if (! this.isTypeOfDD(oDD)) {
19968                         continue;
19969                     }
19970
19971                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19972                         if (this.isOverTarget(pt, oDD, this.mode)) {
19973                             // look for drop interactions
19974                             if (isDrop) {
19975                                 dropEvts.push( oDD );
19976                             // look for drag enter and drag over interactions
19977                             } else {
19978
19979                                 // initial drag over: dragEnter fires
19980                                 if (!oldOvers[oDD.id]) {
19981                                     enterEvts.push( oDD );
19982                                 // subsequent drag overs: dragOver fires
19983                                 } else {
19984                                     overEvts.push( oDD );
19985                                 }
19986
19987                                 this.dragOvers[oDD.id] = oDD;
19988                             }
19989                         }
19990                     }
19991                 }
19992             }
19993
19994             if (this.mode) {
19995                 if (outEvts.length) {
19996                     dc.b4DragOut(e, outEvts);
19997                     dc.onDragOut(e, outEvts);
19998                 }
19999
20000                 if (enterEvts.length) {
20001                     dc.onDragEnter(e, enterEvts);
20002                 }
20003
20004                 if (overEvts.length) {
20005                     dc.b4DragOver(e, overEvts);
20006                     dc.onDragOver(e, overEvts);
20007                 }
20008
20009                 if (dropEvts.length) {
20010                     dc.b4DragDrop(e, dropEvts);
20011                     dc.onDragDrop(e, dropEvts);
20012                 }
20013
20014             } else {
20015                 // fire dragout events
20016                 var len = 0;
20017                 for (i=0, len=outEvts.length; i<len; ++i) {
20018                     dc.b4DragOut(e, outEvts[i].id);
20019                     dc.onDragOut(e, outEvts[i].id);
20020                 }
20021
20022                 // fire enter events
20023                 for (i=0,len=enterEvts.length; i<len; ++i) {
20024                     // dc.b4DragEnter(e, oDD.id);
20025                     dc.onDragEnter(e, enterEvts[i].id);
20026                 }
20027
20028                 // fire over events
20029                 for (i=0,len=overEvts.length; i<len; ++i) {
20030                     dc.b4DragOver(e, overEvts[i].id);
20031                     dc.onDragOver(e, overEvts[i].id);
20032                 }
20033
20034                 // fire drop events
20035                 for (i=0, len=dropEvts.length; i<len; ++i) {
20036                     dc.b4DragDrop(e, dropEvts[i].id);
20037                     dc.onDragDrop(e, dropEvts[i].id);
20038                 }
20039
20040             }
20041
20042             // notify about a drop that did not find a target
20043             if (isDrop && !dropEvts.length) {
20044                 dc.onInvalidDrop(e);
20045             }
20046
20047         },
20048
20049         /**
20050          * Helper function for getting the best match from the list of drag
20051          * and drop objects returned by the drag and drop events when we are
20052          * in INTERSECT mode.  It returns either the first object that the
20053          * cursor is over, or the object that has the greatest overlap with
20054          * the dragged element.
20055          * @method getBestMatch
20056          * @param  {DragDrop[]} dds The array of drag and drop objects
20057          * targeted
20058          * @return {DragDrop}       The best single match
20059          * @static
20060          */
20061         getBestMatch: function(dds) {
20062             var winner = null;
20063             // Return null if the input is not what we expect
20064             //if (!dds || !dds.length || dds.length == 0) {
20065                // winner = null;
20066             // If there is only one item, it wins
20067             //} else if (dds.length == 1) {
20068
20069             var len = dds.length;
20070
20071             if (len == 1) {
20072                 winner = dds[0];
20073             } else {
20074                 // Loop through the targeted items
20075                 for (var i=0; i<len; ++i) {
20076                     var dd = dds[i];
20077                     // If the cursor is over the object, it wins.  If the
20078                     // cursor is over multiple matches, the first one we come
20079                     // to wins.
20080                     if (dd.cursorIsOver) {
20081                         winner = dd;
20082                         break;
20083                     // Otherwise the object with the most overlap wins
20084                     } else {
20085                         if (!winner ||
20086                             winner.overlap.getArea() < dd.overlap.getArea()) {
20087                             winner = dd;
20088                         }
20089                     }
20090                 }
20091             }
20092
20093             return winner;
20094         },
20095
20096         /**
20097          * Refreshes the cache of the top-left and bottom-right points of the
20098          * drag and drop objects in the specified group(s).  This is in the
20099          * format that is stored in the drag and drop instance, so typical
20100          * usage is:
20101          * <code>
20102          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20103          * </code>
20104          * Alternatively:
20105          * <code>
20106          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20107          * </code>
20108          * @TODO this really should be an indexed array.  Alternatively this
20109          * method could accept both.
20110          * @method refreshCache
20111          * @param {Object} groups an associative array of groups to refresh
20112          * @static
20113          */
20114         refreshCache: function(groups) {
20115             for (var sGroup in groups) {
20116                 if ("string" != typeof sGroup) {
20117                     continue;
20118                 }
20119                 for (var i in this.ids[sGroup]) {
20120                     var oDD = this.ids[sGroup][i];
20121
20122                     if (this.isTypeOfDD(oDD)) {
20123                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20124                         var loc = this.getLocation(oDD);
20125                         if (loc) {
20126                             this.locationCache[oDD.id] = loc;
20127                         } else {
20128                             delete this.locationCache[oDD.id];
20129                             // this will unregister the drag and drop object if
20130                             // the element is not in a usable state
20131                             // oDD.unreg();
20132                         }
20133                     }
20134                 }
20135             }
20136         },
20137
20138         /**
20139          * This checks to make sure an element exists and is in the DOM.  The
20140          * main purpose is to handle cases where innerHTML is used to remove
20141          * drag and drop objects from the DOM.  IE provides an 'unspecified
20142          * error' when trying to access the offsetParent of such an element
20143          * @method verifyEl
20144          * @param {HTMLElement} el the element to check
20145          * @return {boolean} true if the element looks usable
20146          * @static
20147          */
20148         verifyEl: function(el) {
20149             if (el) {
20150                 var parent;
20151                 if(Roo.isIE){
20152                     try{
20153                         parent = el.offsetParent;
20154                     }catch(e){}
20155                 }else{
20156                     parent = el.offsetParent;
20157                 }
20158                 if (parent) {
20159                     return true;
20160                 }
20161             }
20162
20163             return false;
20164         },
20165
20166         /**
20167          * Returns a Region object containing the drag and drop element's position
20168          * and size, including the padding configured for it
20169          * @method getLocation
20170          * @param {DragDrop} oDD the drag and drop object to get the
20171          *                       location for
20172          * @return {Roo.lib.Region} a Region object representing the total area
20173          *                             the element occupies, including any padding
20174          *                             the instance is configured for.
20175          * @static
20176          */
20177         getLocation: function(oDD) {
20178             if (! this.isTypeOfDD(oDD)) {
20179                 return null;
20180             }
20181
20182             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20183
20184             try {
20185                 pos= Roo.lib.Dom.getXY(el);
20186             } catch (e) { }
20187
20188             if (!pos) {
20189                 return null;
20190             }
20191
20192             x1 = pos[0];
20193             x2 = x1 + el.offsetWidth;
20194             y1 = pos[1];
20195             y2 = y1 + el.offsetHeight;
20196
20197             t = y1 - oDD.padding[0];
20198             r = x2 + oDD.padding[1];
20199             b = y2 + oDD.padding[2];
20200             l = x1 - oDD.padding[3];
20201
20202             return new Roo.lib.Region( t, r, b, l );
20203         },
20204
20205         /**
20206          * Checks the cursor location to see if it over the target
20207          * @method isOverTarget
20208          * @param {Roo.lib.Point} pt The point to evaluate
20209          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20210          * @return {boolean} true if the mouse is over the target
20211          * @private
20212          * @static
20213          */
20214         isOverTarget: function(pt, oTarget, intersect) {
20215             // use cache if available
20216             var loc = this.locationCache[oTarget.id];
20217             if (!loc || !this.useCache) {
20218                 loc = this.getLocation(oTarget);
20219                 this.locationCache[oTarget.id] = loc;
20220
20221             }
20222
20223             if (!loc) {
20224                 return false;
20225             }
20226
20227             oTarget.cursorIsOver = loc.contains( pt );
20228
20229             // DragDrop is using this as a sanity check for the initial mousedown
20230             // in this case we are done.  In POINT mode, if the drag obj has no
20231             // contraints, we are also done. Otherwise we need to evaluate the
20232             // location of the target as related to the actual location of the
20233             // dragged element.
20234             var dc = this.dragCurrent;
20235             if (!dc || !dc.getTargetCoord ||
20236                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20237                 return oTarget.cursorIsOver;
20238             }
20239
20240             oTarget.overlap = null;
20241
20242             // Get the current location of the drag element, this is the
20243             // location of the mouse event less the delta that represents
20244             // where the original mousedown happened on the element.  We
20245             // need to consider constraints and ticks as well.
20246             var pos = dc.getTargetCoord(pt.x, pt.y);
20247
20248             var el = dc.getDragEl();
20249             var curRegion = new Roo.lib.Region( pos.y,
20250                                                    pos.x + el.offsetWidth,
20251                                                    pos.y + el.offsetHeight,
20252                                                    pos.x );
20253
20254             var overlap = curRegion.intersect(loc);
20255
20256             if (overlap) {
20257                 oTarget.overlap = overlap;
20258                 return (intersect) ? true : oTarget.cursorIsOver;
20259             } else {
20260                 return false;
20261             }
20262         },
20263
20264         /**
20265          * unload event handler
20266          * @method _onUnload
20267          * @private
20268          * @static
20269          */
20270         _onUnload: function(e, me) {
20271             Roo.dd.DragDropMgr.unregAll();
20272         },
20273
20274         /**
20275          * Cleans up the drag and drop events and objects.
20276          * @method unregAll
20277          * @private
20278          * @static
20279          */
20280         unregAll: function() {
20281
20282             if (this.dragCurrent) {
20283                 this.stopDrag();
20284                 this.dragCurrent = null;
20285             }
20286
20287             this._execOnAll("unreg", []);
20288
20289             for (i in this.elementCache) {
20290                 delete this.elementCache[i];
20291             }
20292
20293             this.elementCache = {};
20294             this.ids = {};
20295         },
20296
20297         /**
20298          * A cache of DOM elements
20299          * @property elementCache
20300          * @private
20301          * @static
20302          */
20303         elementCache: {},
20304
20305         /**
20306          * Get the wrapper for the DOM element specified
20307          * @method getElWrapper
20308          * @param {String} id the id of the element to get
20309          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20310          * @private
20311          * @deprecated This wrapper isn't that useful
20312          * @static
20313          */
20314         getElWrapper: function(id) {
20315             var oWrapper = this.elementCache[id];
20316             if (!oWrapper || !oWrapper.el) {
20317                 oWrapper = this.elementCache[id] =
20318                     new this.ElementWrapper(Roo.getDom(id));
20319             }
20320             return oWrapper;
20321         },
20322
20323         /**
20324          * Returns the actual DOM element
20325          * @method getElement
20326          * @param {String} id the id of the elment to get
20327          * @return {Object} The element
20328          * @deprecated use Roo.getDom instead
20329          * @static
20330          */
20331         getElement: function(id) {
20332             return Roo.getDom(id);
20333         },
20334
20335         /**
20336          * Returns the style property for the DOM element (i.e.,
20337          * document.getElById(id).style)
20338          * @method getCss
20339          * @param {String} id the id of the elment to get
20340          * @return {Object} The style property of the element
20341          * @deprecated use Roo.getDom instead
20342          * @static
20343          */
20344         getCss: function(id) {
20345             var el = Roo.getDom(id);
20346             return (el) ? el.style : null;
20347         },
20348
20349         /**
20350          * Inner class for cached elements
20351          * @class DragDropMgr.ElementWrapper
20352          * @for DragDropMgr
20353          * @private
20354          * @deprecated
20355          */
20356         ElementWrapper: function(el) {
20357                 /**
20358                  * The element
20359                  * @property el
20360                  */
20361                 this.el = el || null;
20362                 /**
20363                  * The element id
20364                  * @property id
20365                  */
20366                 this.id = this.el && el.id;
20367                 /**
20368                  * A reference to the style property
20369                  * @property css
20370                  */
20371                 this.css = this.el && el.style;
20372             },
20373
20374         /**
20375          * Returns the X position of an html element
20376          * @method getPosX
20377          * @param el the element for which to get the position
20378          * @return {int} the X coordinate
20379          * @for DragDropMgr
20380          * @deprecated use Roo.lib.Dom.getX instead
20381          * @static
20382          */
20383         getPosX: function(el) {
20384             return Roo.lib.Dom.getX(el);
20385         },
20386
20387         /**
20388          * Returns the Y position of an html element
20389          * @method getPosY
20390          * @param el the element for which to get the position
20391          * @return {int} the Y coordinate
20392          * @deprecated use Roo.lib.Dom.getY instead
20393          * @static
20394          */
20395         getPosY: function(el) {
20396             return Roo.lib.Dom.getY(el);
20397         },
20398
20399         /**
20400          * Swap two nodes.  In IE, we use the native method, for others we
20401          * emulate the IE behavior
20402          * @method swapNode
20403          * @param n1 the first node to swap
20404          * @param n2 the other node to swap
20405          * @static
20406          */
20407         swapNode: function(n1, n2) {
20408             if (n1.swapNode) {
20409                 n1.swapNode(n2);
20410             } else {
20411                 var p = n2.parentNode;
20412                 var s = n2.nextSibling;
20413
20414                 if (s == n1) {
20415                     p.insertBefore(n1, n2);
20416                 } else if (n2 == n1.nextSibling) {
20417                     p.insertBefore(n2, n1);
20418                 } else {
20419                     n1.parentNode.replaceChild(n2, n1);
20420                     p.insertBefore(n1, s);
20421                 }
20422             }
20423         },
20424
20425         /**
20426          * Returns the current scroll position
20427          * @method getScroll
20428          * @private
20429          * @static
20430          */
20431         getScroll: function () {
20432             var t, l, dde=document.documentElement, db=document.body;
20433             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20434                 t = dde.scrollTop;
20435                 l = dde.scrollLeft;
20436             } else if (db) {
20437                 t = db.scrollTop;
20438                 l = db.scrollLeft;
20439             } else {
20440
20441             }
20442             return { top: t, left: l };
20443         },
20444
20445         /**
20446          * Returns the specified element style property
20447          * @method getStyle
20448          * @param {HTMLElement} el          the element
20449          * @param {string}      styleProp   the style property
20450          * @return {string} The value of the style property
20451          * @deprecated use Roo.lib.Dom.getStyle
20452          * @static
20453          */
20454         getStyle: function(el, styleProp) {
20455             return Roo.fly(el).getStyle(styleProp);
20456         },
20457
20458         /**
20459          * Gets the scrollTop
20460          * @method getScrollTop
20461          * @return {int} the document's scrollTop
20462          * @static
20463          */
20464         getScrollTop: function () { return this.getScroll().top; },
20465
20466         /**
20467          * Gets the scrollLeft
20468          * @method getScrollLeft
20469          * @return {int} the document's scrollTop
20470          * @static
20471          */
20472         getScrollLeft: function () { return this.getScroll().left; },
20473
20474         /**
20475          * Sets the x/y position of an element to the location of the
20476          * target element.
20477          * @method moveToEl
20478          * @param {HTMLElement} moveEl      The element to move
20479          * @param {HTMLElement} targetEl    The position reference element
20480          * @static
20481          */
20482         moveToEl: function (moveEl, targetEl) {
20483             var aCoord = Roo.lib.Dom.getXY(targetEl);
20484             Roo.lib.Dom.setXY(moveEl, aCoord);
20485         },
20486
20487         /**
20488          * Numeric array sort function
20489          * @method numericSort
20490          * @static
20491          */
20492         numericSort: function(a, b) { return (a - b); },
20493
20494         /**
20495          * Internal counter
20496          * @property _timeoutCount
20497          * @private
20498          * @static
20499          */
20500         _timeoutCount: 0,
20501
20502         /**
20503          * Trying to make the load order less important.  Without this we get
20504          * an error if this file is loaded before the Event Utility.
20505          * @method _addListeners
20506          * @private
20507          * @static
20508          */
20509         _addListeners: function() {
20510             var DDM = Roo.dd.DDM;
20511             if ( Roo.lib.Event && document ) {
20512                 DDM._onLoad();
20513             } else {
20514                 if (DDM._timeoutCount > 2000) {
20515                 } else {
20516                     setTimeout(DDM._addListeners, 10);
20517                     if (document && document.body) {
20518                         DDM._timeoutCount += 1;
20519                     }
20520                 }
20521             }
20522         },
20523
20524         /**
20525          * Recursively searches the immediate parent and all child nodes for
20526          * the handle element in order to determine wheter or not it was
20527          * clicked.
20528          * @method handleWasClicked
20529          * @param node the html element to inspect
20530          * @static
20531          */
20532         handleWasClicked: function(node, id) {
20533             if (this.isHandle(id, node.id)) {
20534                 return true;
20535             } else {
20536                 // check to see if this is a text node child of the one we want
20537                 var p = node.parentNode;
20538
20539                 while (p) {
20540                     if (this.isHandle(id, p.id)) {
20541                         return true;
20542                     } else {
20543                         p = p.parentNode;
20544                     }
20545                 }
20546             }
20547
20548             return false;
20549         }
20550
20551     };
20552
20553 }();
20554
20555 // shorter alias, save a few bytes
20556 Roo.dd.DDM = Roo.dd.DragDropMgr;
20557 Roo.dd.DDM._addListeners();
20558
20559 }/*
20560  * Based on:
20561  * Ext JS Library 1.1.1
20562  * Copyright(c) 2006-2007, Ext JS, LLC.
20563  *
20564  * Originally Released Under LGPL - original licence link has changed is not relivant.
20565  *
20566  * Fork - LGPL
20567  * <script type="text/javascript">
20568  */
20569
20570 /**
20571  * @class Roo.dd.DD
20572  * A DragDrop implementation where the linked element follows the
20573  * mouse cursor during a drag.
20574  * @extends Roo.dd.DragDrop
20575  * @constructor
20576  * @param {String} id the id of the linked element
20577  * @param {String} sGroup the group of related DragDrop items
20578  * @param {object} config an object containing configurable attributes
20579  *                Valid properties for DD:
20580  *                    scroll
20581  */
20582 Roo.dd.DD = function(id, sGroup, config) {
20583     if (id) {
20584         this.init(id, sGroup, config);
20585     }
20586 };
20587
20588 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20589
20590     /**
20591      * When set to true, the utility automatically tries to scroll the browser
20592      * window wehn a drag and drop element is dragged near the viewport boundary.
20593      * Defaults to true.
20594      * @property scroll
20595      * @type boolean
20596      */
20597     scroll: true,
20598
20599     /**
20600      * Sets the pointer offset to the distance between the linked element's top
20601      * left corner and the location the element was clicked
20602      * @method autoOffset
20603      * @param {int} iPageX the X coordinate of the click
20604      * @param {int} iPageY the Y coordinate of the click
20605      */
20606     autoOffset: function(iPageX, iPageY) {
20607         var x = iPageX - this.startPageX;
20608         var y = iPageY - this.startPageY;
20609         this.setDelta(x, y);
20610     },
20611
20612     /**
20613      * Sets the pointer offset.  You can call this directly to force the
20614      * offset to be in a particular location (e.g., pass in 0,0 to set it
20615      * to the center of the object)
20616      * @method setDelta
20617      * @param {int} iDeltaX the distance from the left
20618      * @param {int} iDeltaY the distance from the top
20619      */
20620     setDelta: function(iDeltaX, iDeltaY) {
20621         this.deltaX = iDeltaX;
20622         this.deltaY = iDeltaY;
20623     },
20624
20625     /**
20626      * Sets the drag element to the location of the mousedown or click event,
20627      * maintaining the cursor location relative to the location on the element
20628      * that was clicked.  Override this if you want to place the element in a
20629      * location other than where the cursor is.
20630      * @method setDragElPos
20631      * @param {int} iPageX the X coordinate of the mousedown or drag event
20632      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20633      */
20634     setDragElPos: function(iPageX, iPageY) {
20635         // the first time we do this, we are going to check to make sure
20636         // the element has css positioning
20637
20638         var el = this.getDragEl();
20639         this.alignElWithMouse(el, iPageX, iPageY);
20640     },
20641
20642     /**
20643      * Sets the 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 alignElWithMouse
20648      * @param {HTMLElement} el the element to move
20649      * @param {int} iPageX the X coordinate of the mousedown or drag event
20650      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20651      */
20652     alignElWithMouse: function(el, iPageX, iPageY) {
20653         var oCoord = this.getTargetCoord(iPageX, iPageY);
20654         var fly = el.dom ? el : Roo.fly(el);
20655         if (!this.deltaSetXY) {
20656             var aCoord = [oCoord.x, oCoord.y];
20657             fly.setXY(aCoord);
20658             var newLeft = fly.getLeft(true);
20659             var newTop  = fly.getTop(true);
20660             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20661         } else {
20662             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20663         }
20664
20665         this.cachePosition(oCoord.x, oCoord.y);
20666         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20667         return oCoord;
20668     },
20669
20670     /**
20671      * Saves the most recent position so that we can reset the constraints and
20672      * tick marks on-demand.  We need to know this so that we can calculate the
20673      * number of pixels the element is offset from its original position.
20674      * @method cachePosition
20675      * @param iPageX the current x position (optional, this just makes it so we
20676      * don't have to look it up again)
20677      * @param iPageY the current y position (optional, this just makes it so we
20678      * don't have to look it up again)
20679      */
20680     cachePosition: function(iPageX, iPageY) {
20681         if (iPageX) {
20682             this.lastPageX = iPageX;
20683             this.lastPageY = iPageY;
20684         } else {
20685             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20686             this.lastPageX = aCoord[0];
20687             this.lastPageY = aCoord[1];
20688         }
20689     },
20690
20691     /**
20692      * Auto-scroll the window if the dragged object has been moved beyond the
20693      * visible window boundary.
20694      * @method autoScroll
20695      * @param {int} x the drag element's x position
20696      * @param {int} y the drag element's y position
20697      * @param {int} h the height of the drag element
20698      * @param {int} w the width of the drag element
20699      * @private
20700      */
20701     autoScroll: function(x, y, h, w) {
20702
20703         if (this.scroll) {
20704             // The client height
20705             var clientH = Roo.lib.Dom.getViewWidth();
20706
20707             // The client width
20708             var clientW = Roo.lib.Dom.getViewHeight();
20709
20710             // The amt scrolled down
20711             var st = this.DDM.getScrollTop();
20712
20713             // The amt scrolled right
20714             var sl = this.DDM.getScrollLeft();
20715
20716             // Location of the bottom of the element
20717             var bot = h + y;
20718
20719             // Location of the right of the element
20720             var right = w + x;
20721
20722             // The distance from the cursor to the bottom of the visible area,
20723             // adjusted so that we don't scroll if the cursor is beyond the
20724             // element drag constraints
20725             var toBot = (clientH + st - y - this.deltaY);
20726
20727             // The distance from the cursor to the right of the visible area
20728             var toRight = (clientW + sl - x - this.deltaX);
20729
20730
20731             // How close to the edge the cursor must be before we scroll
20732             // var thresh = (document.all) ? 100 : 40;
20733             var thresh = 40;
20734
20735             // How many pixels to scroll per autoscroll op.  This helps to reduce
20736             // clunky scrolling. IE is more sensitive about this ... it needs this
20737             // value to be higher.
20738             var scrAmt = (document.all) ? 80 : 30;
20739
20740             // Scroll down if we are near the bottom of the visible page and the
20741             // obj extends below the crease
20742             if ( bot > clientH && toBot < thresh ) {
20743                 window.scrollTo(sl, st + scrAmt);
20744             }
20745
20746             // Scroll up if the window is scrolled down and the top of the object
20747             // goes above the top border
20748             if ( y < st && st > 0 && y - st < thresh ) {
20749                 window.scrollTo(sl, st - scrAmt);
20750             }
20751
20752             // Scroll right if the obj is beyond the right border and the cursor is
20753             // near the border.
20754             if ( right > clientW && toRight < thresh ) {
20755                 window.scrollTo(sl + scrAmt, st);
20756             }
20757
20758             // Scroll left if the window has been scrolled to the right and the obj
20759             // extends past the left border
20760             if ( x < sl && sl > 0 && x - sl < thresh ) {
20761                 window.scrollTo(sl - scrAmt, st);
20762             }
20763         }
20764     },
20765
20766     /**
20767      * Finds the location the element should be placed if we want to move
20768      * it to where the mouse location less the click offset would place us.
20769      * @method getTargetCoord
20770      * @param {int} iPageX the X coordinate of the click
20771      * @param {int} iPageY the Y coordinate of the click
20772      * @return an object that contains the coordinates (Object.x and Object.y)
20773      * @private
20774      */
20775     getTargetCoord: function(iPageX, iPageY) {
20776
20777
20778         var x = iPageX - this.deltaX;
20779         var y = iPageY - this.deltaY;
20780
20781         if (this.constrainX) {
20782             if (x < this.minX) { x = this.minX; }
20783             if (x > this.maxX) { x = this.maxX; }
20784         }
20785
20786         if (this.constrainY) {
20787             if (y < this.minY) { y = this.minY; }
20788             if (y > this.maxY) { y = this.maxY; }
20789         }
20790
20791         x = this.getTick(x, this.xTicks);
20792         y = this.getTick(y, this.yTicks);
20793
20794
20795         return {x:x, y:y};
20796     },
20797
20798     /*
20799      * Sets up config options specific to this class. Overrides
20800      * Roo.dd.DragDrop, but all versions of this method through the
20801      * inheritance chain are called
20802      */
20803     applyConfig: function() {
20804         Roo.dd.DD.superclass.applyConfig.call(this);
20805         this.scroll = (this.config.scroll !== false);
20806     },
20807
20808     /*
20809      * Event that fires prior to the onMouseDown event.  Overrides
20810      * Roo.dd.DragDrop.
20811      */
20812     b4MouseDown: function(e) {
20813         // this.resetConstraints();
20814         this.autoOffset(e.getPageX(),
20815                             e.getPageY());
20816     },
20817
20818     /*
20819      * Event that fires prior to the onDrag event.  Overrides
20820      * Roo.dd.DragDrop.
20821      */
20822     b4Drag: function(e) {
20823         this.setDragElPos(e.getPageX(),
20824                             e.getPageY());
20825     },
20826
20827     toString: function() {
20828         return ("DD " + this.id);
20829     }
20830
20831     //////////////////////////////////////////////////////////////////////////
20832     // Debugging ygDragDrop events that can be overridden
20833     //////////////////////////////////////////////////////////////////////////
20834     /*
20835     startDrag: function(x, y) {
20836     },
20837
20838     onDrag: function(e) {
20839     },
20840
20841     onDragEnter: function(e, id) {
20842     },
20843
20844     onDragOver: function(e, id) {
20845     },
20846
20847     onDragOut: function(e, id) {
20848     },
20849
20850     onDragDrop: function(e, id) {
20851     },
20852
20853     endDrag: function(e) {
20854     }
20855
20856     */
20857
20858 });/*
20859  * Based on:
20860  * Ext JS Library 1.1.1
20861  * Copyright(c) 2006-2007, Ext JS, LLC.
20862  *
20863  * Originally Released Under LGPL - original licence link has changed is not relivant.
20864  *
20865  * Fork - LGPL
20866  * <script type="text/javascript">
20867  */
20868
20869 /**
20870  * @class Roo.dd.DDProxy
20871  * A DragDrop implementation that inserts an empty, bordered div into
20872  * the document that follows the cursor during drag operations.  At the time of
20873  * the click, the frame div is resized to the dimensions of the linked html
20874  * element, and moved to the exact location of the linked element.
20875  *
20876  * References to the "frame" element refer to the single proxy element that
20877  * was created to be dragged in place of all DDProxy elements on the
20878  * page.
20879  *
20880  * @extends Roo.dd.DD
20881  * @constructor
20882  * @param {String} id the id of the linked html element
20883  * @param {String} sGroup the group of related DragDrop objects
20884  * @param {object} config an object containing configurable attributes
20885  *                Valid properties for DDProxy in addition to those in DragDrop:
20886  *                   resizeFrame, centerFrame, dragElId
20887  */
20888 Roo.dd.DDProxy = function(id, sGroup, config) {
20889     if (id) {
20890         this.init(id, sGroup, config);
20891         this.initFrame();
20892     }
20893 };
20894
20895 /**
20896  * The default drag frame div id
20897  * @property Roo.dd.DDProxy.dragElId
20898  * @type String
20899  * @static
20900  */
20901 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20902
20903 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20904
20905     /**
20906      * By default we resize the drag frame to be the same size as the element
20907      * we want to drag (this is to get the frame effect).  We can turn it off
20908      * if we want a different behavior.
20909      * @property resizeFrame
20910      * @type boolean
20911      */
20912     resizeFrame: true,
20913
20914     /**
20915      * By default the frame is positioned exactly where the drag element is, so
20916      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20917      * you do not have constraints on the obj is to have the drag frame centered
20918      * around the cursor.  Set centerFrame to true for this effect.
20919      * @property centerFrame
20920      * @type boolean
20921      */
20922     centerFrame: false,
20923
20924     /**
20925      * Creates the proxy element if it does not yet exist
20926      * @method createFrame
20927      */
20928     createFrame: function() {
20929         var self = this;
20930         var body = document.body;
20931
20932         if (!body || !body.firstChild) {
20933             setTimeout( function() { self.createFrame(); }, 50 );
20934             return;
20935         }
20936
20937         var div = this.getDragEl();
20938
20939         if (!div) {
20940             div    = document.createElement("div");
20941             div.id = this.dragElId;
20942             var s  = div.style;
20943
20944             s.position   = "absolute";
20945             s.visibility = "hidden";
20946             s.cursor     = "move";
20947             s.border     = "2px solid #aaa";
20948             s.zIndex     = 999;
20949
20950             // appendChild can blow up IE if invoked prior to the window load event
20951             // while rendering a table.  It is possible there are other scenarios
20952             // that would cause this to happen as well.
20953             body.insertBefore(div, body.firstChild);
20954         }
20955     },
20956
20957     /**
20958      * Initialization for the drag frame element.  Must be called in the
20959      * constructor of all subclasses
20960      * @method initFrame
20961      */
20962     initFrame: function() {
20963         this.createFrame();
20964     },
20965
20966     applyConfig: function() {
20967         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20968
20969         this.resizeFrame = (this.config.resizeFrame !== false);
20970         this.centerFrame = (this.config.centerFrame);
20971         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20972     },
20973
20974     /**
20975      * Resizes the drag frame to the dimensions of the clicked object, positions
20976      * it over the object, and finally displays it
20977      * @method showFrame
20978      * @param {int} iPageX X click position
20979      * @param {int} iPageY Y click position
20980      * @private
20981      */
20982     showFrame: function(iPageX, iPageY) {
20983         var el = this.getEl();
20984         var dragEl = this.getDragEl();
20985         var s = dragEl.style;
20986
20987         this._resizeProxy();
20988
20989         if (this.centerFrame) {
20990             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20991                            Math.round(parseInt(s.height, 10)/2) );
20992         }
20993
20994         this.setDragElPos(iPageX, iPageY);
20995
20996         Roo.fly(dragEl).show();
20997     },
20998
20999     /**
21000      * The proxy is automatically resized to the dimensions of the linked
21001      * element when a drag is initiated, unless resizeFrame is set to false
21002      * @method _resizeProxy
21003      * @private
21004      */
21005     _resizeProxy: function() {
21006         if (this.resizeFrame) {
21007             var el = this.getEl();
21008             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21009         }
21010     },
21011
21012     // overrides Roo.dd.DragDrop
21013     b4MouseDown: function(e) {
21014         var x = e.getPageX();
21015         var y = e.getPageY();
21016         this.autoOffset(x, y);
21017         this.setDragElPos(x, y);
21018     },
21019
21020     // overrides Roo.dd.DragDrop
21021     b4StartDrag: function(x, y) {
21022         // show the drag frame
21023         this.showFrame(x, y);
21024     },
21025
21026     // overrides Roo.dd.DragDrop
21027     b4EndDrag: function(e) {
21028         Roo.fly(this.getDragEl()).hide();
21029     },
21030
21031     // overrides Roo.dd.DragDrop
21032     // By default we try to move the element to the last location of the frame.
21033     // This is so that the default behavior mirrors that of Roo.dd.DD.
21034     endDrag: function(e) {
21035
21036         var lel = this.getEl();
21037         var del = this.getDragEl();
21038
21039         // Show the drag frame briefly so we can get its position
21040         del.style.visibility = "";
21041
21042         this.beforeMove();
21043         // Hide the linked element before the move to get around a Safari
21044         // rendering bug.
21045         lel.style.visibility = "hidden";
21046         Roo.dd.DDM.moveToEl(lel, del);
21047         del.style.visibility = "hidden";
21048         lel.style.visibility = "";
21049
21050         this.afterDrag();
21051     },
21052
21053     beforeMove : function(){
21054
21055     },
21056
21057     afterDrag : function(){
21058
21059     },
21060
21061     toString: function() {
21062         return ("DDProxy " + this.id);
21063     }
21064
21065 });
21066 /*
21067  * Based on:
21068  * Ext JS Library 1.1.1
21069  * Copyright(c) 2006-2007, Ext JS, LLC.
21070  *
21071  * Originally Released Under LGPL - original licence link has changed is not relivant.
21072  *
21073  * Fork - LGPL
21074  * <script type="text/javascript">
21075  */
21076
21077  /**
21078  * @class Roo.dd.DDTarget
21079  * A DragDrop implementation that does not move, but can be a drop
21080  * target.  You would get the same result by simply omitting implementation
21081  * for the event callbacks, but this way we reduce the processing cost of the
21082  * event listener and the callbacks.
21083  * @extends Roo.dd.DragDrop
21084  * @constructor
21085  * @param {String} id the id of the element that is a drop target
21086  * @param {String} sGroup the group of related DragDrop objects
21087  * @param {object} config an object containing configurable attributes
21088  *                 Valid properties for DDTarget in addition to those in
21089  *                 DragDrop:
21090  *                    none
21091  */
21092 Roo.dd.DDTarget = function(id, sGroup, config) {
21093     if (id) {
21094         this.initTarget(id, sGroup, config);
21095     }
21096     if (config.listeners || config.events) { 
21097        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21098             listeners : config.listeners || {}, 
21099             events : config.events || {} 
21100         });    
21101     }
21102 };
21103
21104 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21105 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21106     toString: function() {
21107         return ("DDTarget " + this.id);
21108     }
21109 });
21110 /*
21111  * Based on:
21112  * Ext JS Library 1.1.1
21113  * Copyright(c) 2006-2007, Ext JS, LLC.
21114  *
21115  * Originally Released Under LGPL - original licence link has changed is not relivant.
21116  *
21117  * Fork - LGPL
21118  * <script type="text/javascript">
21119  */
21120  
21121
21122 /**
21123  * @class Roo.dd.ScrollManager
21124  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21125  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21126  * @singleton
21127  */
21128 Roo.dd.ScrollManager = function(){
21129     var ddm = Roo.dd.DragDropMgr;
21130     var els = {};
21131     var dragEl = null;
21132     var proc = {};
21133     
21134     
21135     
21136     var onStop = function(e){
21137         dragEl = null;
21138         clearProc();
21139     };
21140     
21141     var triggerRefresh = function(){
21142         if(ddm.dragCurrent){
21143              ddm.refreshCache(ddm.dragCurrent.groups);
21144         }
21145     };
21146     
21147     var doScroll = function(){
21148         if(ddm.dragCurrent){
21149             var dds = Roo.dd.ScrollManager;
21150             if(!dds.animate){
21151                 if(proc.el.scroll(proc.dir, dds.increment)){
21152                     triggerRefresh();
21153                 }
21154             }else{
21155                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21156             }
21157         }
21158     };
21159     
21160     var clearProc = function(){
21161         if(proc.id){
21162             clearInterval(proc.id);
21163         }
21164         proc.id = 0;
21165         proc.el = null;
21166         proc.dir = "";
21167     };
21168     
21169     var startProc = function(el, dir){
21170          Roo.log('scroll startproc');
21171         clearProc();
21172         proc.el = el;
21173         proc.dir = dir;
21174         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21175     };
21176     
21177     var onFire = function(e, isDrop){
21178        
21179         if(isDrop || !ddm.dragCurrent){ return; }
21180         var dds = Roo.dd.ScrollManager;
21181         if(!dragEl || dragEl != ddm.dragCurrent){
21182             dragEl = ddm.dragCurrent;
21183             // refresh regions on drag start
21184             dds.refreshCache();
21185         }
21186         
21187         var xy = Roo.lib.Event.getXY(e);
21188         var pt = new Roo.lib.Point(xy[0], xy[1]);
21189         for(var id in els){
21190             var el = els[id], r = el._region;
21191             if(r && r.contains(pt) && el.isScrollable()){
21192                 if(r.bottom - pt.y <= dds.thresh){
21193                     if(proc.el != el){
21194                         startProc(el, "down");
21195                     }
21196                     return;
21197                 }else if(r.right - pt.x <= dds.thresh){
21198                     if(proc.el != el){
21199                         startProc(el, "left");
21200                     }
21201                     return;
21202                 }else if(pt.y - r.top <= dds.thresh){
21203                     if(proc.el != el){
21204                         startProc(el, "up");
21205                     }
21206                     return;
21207                 }else if(pt.x - r.left <= dds.thresh){
21208                     if(proc.el != el){
21209                         startProc(el, "right");
21210                     }
21211                     return;
21212                 }
21213             }
21214         }
21215         clearProc();
21216     };
21217     
21218     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21219     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21220     
21221     return {
21222         /**
21223          * Registers new overflow element(s) to auto scroll
21224          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21225          */
21226         register : function(el){
21227             if(el instanceof Array){
21228                 for(var i = 0, len = el.length; i < len; i++) {
21229                         this.register(el[i]);
21230                 }
21231             }else{
21232                 el = Roo.get(el);
21233                 els[el.id] = el;
21234             }
21235             Roo.dd.ScrollManager.els = els;
21236         },
21237         
21238         /**
21239          * Unregisters overflow element(s) so they are no longer scrolled
21240          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21241          */
21242         unregister : function(el){
21243             if(el instanceof Array){
21244                 for(var i = 0, len = el.length; i < len; i++) {
21245                         this.unregister(el[i]);
21246                 }
21247             }else{
21248                 el = Roo.get(el);
21249                 delete els[el.id];
21250             }
21251         },
21252         
21253         /**
21254          * The number of pixels from the edge of a container the pointer needs to be to 
21255          * trigger scrolling (defaults to 25)
21256          * @type Number
21257          */
21258         thresh : 25,
21259         
21260         /**
21261          * The number of pixels to scroll in each scroll increment (defaults to 50)
21262          * @type Number
21263          */
21264         increment : 100,
21265         
21266         /**
21267          * The frequency of scrolls in milliseconds (defaults to 500)
21268          * @type Number
21269          */
21270         frequency : 500,
21271         
21272         /**
21273          * True to animate the scroll (defaults to true)
21274          * @type Boolean
21275          */
21276         animate: true,
21277         
21278         /**
21279          * The animation duration in seconds - 
21280          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21281          * @type Number
21282          */
21283         animDuration: .4,
21284         
21285         /**
21286          * Manually trigger a cache refresh.
21287          */
21288         refreshCache : function(){
21289             for(var id in els){
21290                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21291                     els[id]._region = els[id].getRegion();
21292                 }
21293             }
21294         }
21295     };
21296 }();/*
21297  * Based on:
21298  * Ext JS Library 1.1.1
21299  * Copyright(c) 2006-2007, Ext JS, LLC.
21300  *
21301  * Originally Released Under LGPL - original licence link has changed is not relivant.
21302  *
21303  * Fork - LGPL
21304  * <script type="text/javascript">
21305  */
21306  
21307
21308 /**
21309  * @class Roo.dd.Registry
21310  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21311  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21312  * @singleton
21313  */
21314 Roo.dd.Registry = function(){
21315     var elements = {}; 
21316     var handles = {}; 
21317     var autoIdSeed = 0;
21318
21319     var getId = function(el, autogen){
21320         if(typeof el == "string"){
21321             return el;
21322         }
21323         var id = el.id;
21324         if(!id && autogen !== false){
21325             id = "roodd-" + (++autoIdSeed);
21326             el.id = id;
21327         }
21328         return id;
21329     };
21330     
21331     return {
21332     /**
21333      * Register a drag drop element
21334      * @param {String|HTMLElement} element The id or DOM node to register
21335      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21336      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21337      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21338      * populated in the data object (if applicable):
21339      * <pre>
21340 Value      Description<br />
21341 ---------  ------------------------------------------<br />
21342 handles    Array of DOM nodes that trigger dragging<br />
21343            for the element being registered<br />
21344 isHandle   True if the element passed in triggers<br />
21345            dragging itself, else false
21346 </pre>
21347      */
21348         register : function(el, data){
21349             data = data || {};
21350             if(typeof el == "string"){
21351                 el = document.getElementById(el);
21352             }
21353             data.ddel = el;
21354             elements[getId(el)] = data;
21355             if(data.isHandle !== false){
21356                 handles[data.ddel.id] = data;
21357             }
21358             if(data.handles){
21359                 var hs = data.handles;
21360                 for(var i = 0, len = hs.length; i < len; i++){
21361                         handles[getId(hs[i])] = data;
21362                 }
21363             }
21364         },
21365
21366     /**
21367      * Unregister a drag drop element
21368      * @param {String|HTMLElement}  element The id or DOM node to unregister
21369      */
21370         unregister : function(el){
21371             var id = getId(el, false);
21372             var data = elements[id];
21373             if(data){
21374                 delete elements[id];
21375                 if(data.handles){
21376                     var hs = data.handles;
21377                     for(var i = 0, len = hs.length; i < len; i++){
21378                         delete handles[getId(hs[i], false)];
21379                     }
21380                 }
21381             }
21382         },
21383
21384     /**
21385      * Returns the handle registered for a DOM Node by id
21386      * @param {String|HTMLElement} id The DOM node or id to look up
21387      * @return {Object} handle The custom handle data
21388      */
21389         getHandle : function(id){
21390             if(typeof id != "string"){ // must be element?
21391                 id = id.id;
21392             }
21393             return handles[id];
21394         },
21395
21396     /**
21397      * Returns the handle that is registered for the DOM node that is the target of the event
21398      * @param {Event} e The event
21399      * @return {Object} handle The custom handle data
21400      */
21401         getHandleFromEvent : function(e){
21402             var t = Roo.lib.Event.getTarget(e);
21403             return t ? handles[t.id] : null;
21404         },
21405
21406     /**
21407      * Returns a custom data object that is registered for a DOM node by id
21408      * @param {String|HTMLElement} id The DOM node or id to look up
21409      * @return {Object} data The custom data
21410      */
21411         getTarget : function(id){
21412             if(typeof id != "string"){ // must be element?
21413                 id = id.id;
21414             }
21415             return elements[id];
21416         },
21417
21418     /**
21419      * Returns a custom data object that is registered for the DOM node that is the target of the event
21420      * @param {Event} e The event
21421      * @return {Object} data The custom data
21422      */
21423         getTargetFromEvent : function(e){
21424             var t = Roo.lib.Event.getTarget(e);
21425             return t ? elements[t.id] || handles[t.id] : null;
21426         }
21427     };
21428 }();/*
21429  * Based on:
21430  * Ext JS Library 1.1.1
21431  * Copyright(c) 2006-2007, Ext JS, LLC.
21432  *
21433  * Originally Released Under LGPL - original licence link has changed is not relivant.
21434  *
21435  * Fork - LGPL
21436  * <script type="text/javascript">
21437  */
21438  
21439
21440 /**
21441  * @class Roo.dd.StatusProxy
21442  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21443  * default drag proxy used by all Roo.dd components.
21444  * @constructor
21445  * @param {Object} config
21446  */
21447 Roo.dd.StatusProxy = function(config){
21448     Roo.apply(this, config);
21449     this.id = this.id || Roo.id();
21450     this.el = new Roo.Layer({
21451         dh: {
21452             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21453                 {tag: "div", cls: "x-dd-drop-icon"},
21454                 {tag: "div", cls: "x-dd-drag-ghost"}
21455             ]
21456         }, 
21457         shadow: !config || config.shadow !== false
21458     });
21459     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21460     this.dropStatus = this.dropNotAllowed;
21461 };
21462
21463 Roo.dd.StatusProxy.prototype = {
21464     /**
21465      * @cfg {String} dropAllowed
21466      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21467      */
21468     dropAllowed : "x-dd-drop-ok",
21469     /**
21470      * @cfg {String} dropNotAllowed
21471      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21472      */
21473     dropNotAllowed : "x-dd-drop-nodrop",
21474
21475     /**
21476      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21477      * over the current target element.
21478      * @param {String} cssClass The css class for the new drop status indicator image
21479      */
21480     setStatus : function(cssClass){
21481         cssClass = cssClass || this.dropNotAllowed;
21482         if(this.dropStatus != cssClass){
21483             this.el.replaceClass(this.dropStatus, cssClass);
21484             this.dropStatus = cssClass;
21485         }
21486     },
21487
21488     /**
21489      * Resets the status indicator to the default dropNotAllowed value
21490      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21491      */
21492     reset : function(clearGhost){
21493         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21494         this.dropStatus = this.dropNotAllowed;
21495         if(clearGhost){
21496             this.ghost.update("");
21497         }
21498     },
21499
21500     /**
21501      * Updates the contents of the ghost element
21502      * @param {String} html The html that will replace the current innerHTML of the ghost element
21503      */
21504     update : function(html){
21505         if(typeof html == "string"){
21506             this.ghost.update(html);
21507         }else{
21508             this.ghost.update("");
21509             html.style.margin = "0";
21510             this.ghost.dom.appendChild(html);
21511         }
21512         // ensure float = none set?? cant remember why though.
21513         var el = this.ghost.dom.firstChild;
21514                 if(el){
21515                         Roo.fly(el).setStyle('float', 'none');
21516                 }
21517     },
21518     
21519     /**
21520      * Returns the underlying proxy {@link Roo.Layer}
21521      * @return {Roo.Layer} el
21522     */
21523     getEl : function(){
21524         return this.el;
21525     },
21526
21527     /**
21528      * Returns the ghost element
21529      * @return {Roo.Element} el
21530      */
21531     getGhost : function(){
21532         return this.ghost;
21533     },
21534
21535     /**
21536      * Hides the proxy
21537      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21538      */
21539     hide : function(clear){
21540         this.el.hide();
21541         if(clear){
21542             this.reset(true);
21543         }
21544     },
21545
21546     /**
21547      * Stops the repair animation if it's currently running
21548      */
21549     stop : function(){
21550         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21551             this.anim.stop();
21552         }
21553     },
21554
21555     /**
21556      * Displays this proxy
21557      */
21558     show : function(){
21559         this.el.show();
21560     },
21561
21562     /**
21563      * Force the Layer to sync its shadow and shim positions to the element
21564      */
21565     sync : function(){
21566         this.el.sync();
21567     },
21568
21569     /**
21570      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21571      * invalid drop operation by the item being dragged.
21572      * @param {Array} xy The XY position of the element ([x, y])
21573      * @param {Function} callback The function to call after the repair is complete
21574      * @param {Object} scope The scope in which to execute the callback
21575      */
21576     repair : function(xy, callback, scope){
21577         this.callback = callback;
21578         this.scope = scope;
21579         if(xy && this.animRepair !== false){
21580             this.el.addClass("x-dd-drag-repair");
21581             this.el.hideUnders(true);
21582             this.anim = this.el.shift({
21583                 duration: this.repairDuration || .5,
21584                 easing: 'easeOut',
21585                 xy: xy,
21586                 stopFx: true,
21587                 callback: this.afterRepair,
21588                 scope: this
21589             });
21590         }else{
21591             this.afterRepair();
21592         }
21593     },
21594
21595     // private
21596     afterRepair : function(){
21597         this.hide(true);
21598         if(typeof this.callback == "function"){
21599             this.callback.call(this.scope || this);
21600         }
21601         this.callback = null;
21602         this.scope = null;
21603     }
21604 };/*
21605  * Based on:
21606  * Ext JS Library 1.1.1
21607  * Copyright(c) 2006-2007, Ext JS, LLC.
21608  *
21609  * Originally Released Under LGPL - original licence link has changed is not relivant.
21610  *
21611  * Fork - LGPL
21612  * <script type="text/javascript">
21613  */
21614
21615 /**
21616  * @class Roo.dd.DragSource
21617  * @extends Roo.dd.DDProxy
21618  * A simple class that provides the basic implementation needed to make any element draggable.
21619  * @constructor
21620  * @param {String/HTMLElement/Element} el The container element
21621  * @param {Object} config
21622  */
21623 Roo.dd.DragSource = function(el, config){
21624     this.el = Roo.get(el);
21625     this.dragData = {};
21626     
21627     Roo.apply(this, config);
21628     
21629     if(!this.proxy){
21630         this.proxy = new Roo.dd.StatusProxy();
21631     }
21632
21633     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21634           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21635     
21636     this.dragging = false;
21637 };
21638
21639 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21640     /**
21641      * @cfg {String} dropAllowed
21642      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21643      */
21644     dropAllowed : "x-dd-drop-ok",
21645     /**
21646      * @cfg {String} dropNotAllowed
21647      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21648      */
21649     dropNotAllowed : "x-dd-drop-nodrop",
21650
21651     /**
21652      * Returns the data object associated with this drag source
21653      * @return {Object} data An object containing arbitrary data
21654      */
21655     getDragData : function(e){
21656         return this.dragData;
21657     },
21658
21659     // private
21660     onDragEnter : function(e, id){
21661         var target = Roo.dd.DragDropMgr.getDDById(id);
21662         this.cachedTarget = target;
21663         if(this.beforeDragEnter(target, e, id) !== false){
21664             if(target.isNotifyTarget){
21665                 var status = target.notifyEnter(this, e, this.dragData);
21666                 this.proxy.setStatus(status);
21667             }else{
21668                 this.proxy.setStatus(this.dropAllowed);
21669             }
21670             
21671             if(this.afterDragEnter){
21672                 /**
21673                  * An empty function by default, but provided so that you can perform a custom action
21674                  * when the dragged item enters the drop target by providing an implementation.
21675                  * @param {Roo.dd.DragDrop} target The drop target
21676                  * @param {Event} e The event object
21677                  * @param {String} id The id of the dragged element
21678                  * @method afterDragEnter
21679                  */
21680                 this.afterDragEnter(target, e, id);
21681             }
21682         }
21683     },
21684
21685     /**
21686      * An empty function by default, but provided so that you can perform a custom action
21687      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21688      * @param {Roo.dd.DragDrop} target The drop target
21689      * @param {Event} e The event object
21690      * @param {String} id The id of the dragged element
21691      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21692      */
21693     beforeDragEnter : function(target, e, id){
21694         return true;
21695     },
21696
21697     // private
21698     alignElWithMouse: function() {
21699         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21700         this.proxy.sync();
21701     },
21702
21703     // private
21704     onDragOver : function(e, id){
21705         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21706         if(this.beforeDragOver(target, e, id) !== false){
21707             if(target.isNotifyTarget){
21708                 var status = target.notifyOver(this, e, this.dragData);
21709                 this.proxy.setStatus(status);
21710             }
21711
21712             if(this.afterDragOver){
21713                 /**
21714                  * An empty function by default, but provided so that you can perform a custom action
21715                  * while the dragged item is over the drop target by providing an implementation.
21716                  * @param {Roo.dd.DragDrop} target The drop target
21717                  * @param {Event} e The event object
21718                  * @param {String} id The id of the dragged element
21719                  * @method afterDragOver
21720                  */
21721                 this.afterDragOver(target, e, id);
21722             }
21723         }
21724     },
21725
21726     /**
21727      * An empty function by default, but provided so that you can perform a custom action
21728      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21729      * @param {Roo.dd.DragDrop} target The drop target
21730      * @param {Event} e The event object
21731      * @param {String} id The id of the dragged element
21732      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21733      */
21734     beforeDragOver : function(target, e, id){
21735         return true;
21736     },
21737
21738     // private
21739     onDragOut : function(e, id){
21740         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21741         if(this.beforeDragOut(target, e, id) !== false){
21742             if(target.isNotifyTarget){
21743                 target.notifyOut(this, e, this.dragData);
21744             }
21745             this.proxy.reset();
21746             if(this.afterDragOut){
21747                 /**
21748                  * An empty function by default, but provided so that you can perform a custom action
21749                  * after the dragged item is dragged out of the target without dropping.
21750                  * @param {Roo.dd.DragDrop} target The drop target
21751                  * @param {Event} e The event object
21752                  * @param {String} id The id of the dragged element
21753                  * @method afterDragOut
21754                  */
21755                 this.afterDragOut(target, e, id);
21756             }
21757         }
21758         this.cachedTarget = null;
21759     },
21760
21761     /**
21762      * An empty function by default, but provided so that you can perform a custom action before the dragged
21763      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21764      * @param {Roo.dd.DragDrop} target The drop target
21765      * @param {Event} e The event object
21766      * @param {String} id The id of the dragged element
21767      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21768      */
21769     beforeDragOut : function(target, e, id){
21770         return true;
21771     },
21772     
21773     // private
21774     onDragDrop : function(e, id){
21775         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21776         if(this.beforeDragDrop(target, e, id) !== false){
21777             if(target.isNotifyTarget){
21778                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21779                     this.onValidDrop(target, e, id);
21780                 }else{
21781                     this.onInvalidDrop(target, e, id);
21782                 }
21783             }else{
21784                 this.onValidDrop(target, e, id);
21785             }
21786             
21787             if(this.afterDragDrop){
21788                 /**
21789                  * An empty function by default, but provided so that you can perform a custom action
21790                  * after a valid drag drop has occurred by providing an implementation.
21791                  * @param {Roo.dd.DragDrop} target The drop target
21792                  * @param {Event} e The event object
21793                  * @param {String} id The id of the dropped element
21794                  * @method afterDragDrop
21795                  */
21796                 this.afterDragDrop(target, e, id);
21797             }
21798         }
21799         delete this.cachedTarget;
21800     },
21801
21802     /**
21803      * An empty function by default, but provided so that you can perform a custom action before the dragged
21804      * item is dropped onto the target and optionally cancel the onDragDrop.
21805      * @param {Roo.dd.DragDrop} target The drop target
21806      * @param {Event} e The event object
21807      * @param {String} id The id of the dragged element
21808      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21809      */
21810     beforeDragDrop : function(target, e, id){
21811         return true;
21812     },
21813
21814     // private
21815     onValidDrop : function(target, e, id){
21816         this.hideProxy();
21817         if(this.afterValidDrop){
21818             /**
21819              * An empty function by default, but provided so that you can perform a custom action
21820              * after a valid drop has occurred by providing an implementation.
21821              * @param {Object} target The target DD 
21822              * @param {Event} e The event object
21823              * @param {String} id The id of the dropped element
21824              * @method afterInvalidDrop
21825              */
21826             this.afterValidDrop(target, e, id);
21827         }
21828     },
21829
21830     // private
21831     getRepairXY : function(e, data){
21832         return this.el.getXY();  
21833     },
21834
21835     // private
21836     onInvalidDrop : function(target, e, id){
21837         this.beforeInvalidDrop(target, e, id);
21838         if(this.cachedTarget){
21839             if(this.cachedTarget.isNotifyTarget){
21840                 this.cachedTarget.notifyOut(this, e, this.dragData);
21841             }
21842             this.cacheTarget = null;
21843         }
21844         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21845
21846         if(this.afterInvalidDrop){
21847             /**
21848              * An empty function by default, but provided so that you can perform a custom action
21849              * after an invalid drop has occurred by providing an implementation.
21850              * @param {Event} e The event object
21851              * @param {String} id The id of the dropped element
21852              * @method afterInvalidDrop
21853              */
21854             this.afterInvalidDrop(e, id);
21855         }
21856     },
21857
21858     // private
21859     afterRepair : function(){
21860         if(Roo.enableFx){
21861             this.el.highlight(this.hlColor || "c3daf9");
21862         }
21863         this.dragging = false;
21864     },
21865
21866     /**
21867      * An empty function by default, but provided so that you can perform a custom action after an invalid
21868      * drop has occurred.
21869      * @param {Roo.dd.DragDrop} target The drop target
21870      * @param {Event} e The event object
21871      * @param {String} id The id of the dragged element
21872      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21873      */
21874     beforeInvalidDrop : function(target, e, id){
21875         return true;
21876     },
21877
21878     // private
21879     handleMouseDown : function(e){
21880         if(this.dragging) {
21881             return;
21882         }
21883         var data = this.getDragData(e);
21884         if(data && this.onBeforeDrag(data, e) !== false){
21885             this.dragData = data;
21886             this.proxy.stop();
21887             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21888         } 
21889     },
21890
21891     /**
21892      * An empty function by default, but provided so that you can perform a custom action before the initial
21893      * drag event begins and optionally cancel it.
21894      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21895      * @param {Event} e The event object
21896      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21897      */
21898     onBeforeDrag : function(data, e){
21899         return true;
21900     },
21901
21902     /**
21903      * An empty function by default, but provided so that you can perform a custom action once the initial
21904      * drag event has begun.  The drag cannot be canceled from this function.
21905      * @param {Number} x The x position of the click on the dragged object
21906      * @param {Number} y The y position of the click on the dragged object
21907      */
21908     onStartDrag : Roo.emptyFn,
21909
21910     // private - YUI override
21911     startDrag : function(x, y){
21912         this.proxy.reset();
21913         this.dragging = true;
21914         this.proxy.update("");
21915         this.onInitDrag(x, y);
21916         this.proxy.show();
21917     },
21918
21919     // private
21920     onInitDrag : function(x, y){
21921         var clone = this.el.dom.cloneNode(true);
21922         clone.id = Roo.id(); // prevent duplicate ids
21923         this.proxy.update(clone);
21924         this.onStartDrag(x, y);
21925         return true;
21926     },
21927
21928     /**
21929      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21930      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21931      */
21932     getProxy : function(){
21933         return this.proxy;  
21934     },
21935
21936     /**
21937      * Hides the drag source's {@link Roo.dd.StatusProxy}
21938      */
21939     hideProxy : function(){
21940         this.proxy.hide();  
21941         this.proxy.reset(true);
21942         this.dragging = false;
21943     },
21944
21945     // private
21946     triggerCacheRefresh : function(){
21947         Roo.dd.DDM.refreshCache(this.groups);
21948     },
21949
21950     // private - override to prevent hiding
21951     b4EndDrag: function(e) {
21952     },
21953
21954     // private - override to prevent moving
21955     endDrag : function(e){
21956         this.onEndDrag(this.dragData, e);
21957     },
21958
21959     // private
21960     onEndDrag : function(data, e){
21961     },
21962     
21963     // private - pin to cursor
21964     autoOffset : function(x, y) {
21965         this.setDelta(-12, -20);
21966     }    
21967 });/*
21968  * Based on:
21969  * Ext JS Library 1.1.1
21970  * Copyright(c) 2006-2007, Ext JS, LLC.
21971  *
21972  * Originally Released Under LGPL - original licence link has changed is not relivant.
21973  *
21974  * Fork - LGPL
21975  * <script type="text/javascript">
21976  */
21977
21978
21979 /**
21980  * @class Roo.dd.DropTarget
21981  * @extends Roo.dd.DDTarget
21982  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21983  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21984  * @constructor
21985  * @param {String/HTMLElement/Element} el The container element
21986  * @param {Object} config
21987  */
21988 Roo.dd.DropTarget = function(el, config){
21989     this.el = Roo.get(el);
21990     
21991     var listeners = false; ;
21992     if (config && config.listeners) {
21993         listeners= config.listeners;
21994         delete config.listeners;
21995     }
21996     Roo.apply(this, config);
21997     
21998     if(this.containerScroll){
21999         Roo.dd.ScrollManager.register(this.el);
22000     }
22001     this.addEvents( {
22002          /**
22003          * @scope Roo.dd.DropTarget
22004          */
22005          
22006          /**
22007          * @event enter
22008          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22009          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22010          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22011          * 
22012          * IMPORTANT : it should set this.overClass and this.dropAllowed
22013          * 
22014          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22015          * @param {Event} e The event
22016          * @param {Object} data An object containing arbitrary data supplied by the drag source
22017          */
22018         "enter" : true,
22019         
22020          /**
22021          * @event over
22022          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22023          * This method will be called on every mouse movement while the drag source is over the drop target.
22024          * This default implementation simply returns the dropAllowed config value.
22025          * 
22026          * IMPORTANT : it should set this.dropAllowed
22027          * 
22028          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22029          * @param {Event} e The event
22030          * @param {Object} data An object containing arbitrary data supplied by the drag source
22031          
22032          */
22033         "over" : true,
22034         /**
22035          * @event out
22036          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22037          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22038          * overClass (if any) from the drop element.
22039          * 
22040          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22041          * @param {Event} e The event
22042          * @param {Object} data An object containing arbitrary data supplied by the drag source
22043          */
22044          "out" : true,
22045          
22046         /**
22047          * @event drop
22048          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22049          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22050          * implementation that does something to process the drop event and returns true so that the drag source's
22051          * repair action does not run.
22052          * 
22053          * IMPORTANT : it should set this.success
22054          * 
22055          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22056          * @param {Event} e The event
22057          * @param {Object} data An object containing arbitrary data supplied by the drag source
22058         */
22059          "drop" : true
22060     });
22061             
22062      
22063     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22064         this.el.dom, 
22065         this.ddGroup || this.group,
22066         {
22067             isTarget: true,
22068             listeners : listeners || {} 
22069            
22070         
22071         }
22072     );
22073
22074 };
22075
22076 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22077     /**
22078      * @cfg {String} overClass
22079      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22080      */
22081      /**
22082      * @cfg {String} ddGroup
22083      * The drag drop group to handle drop events for
22084      */
22085      
22086     /**
22087      * @cfg {String} dropAllowed
22088      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22089      */
22090     dropAllowed : "x-dd-drop-ok",
22091     /**
22092      * @cfg {String} dropNotAllowed
22093      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22094      */
22095     dropNotAllowed : "x-dd-drop-nodrop",
22096     /**
22097      * @cfg {boolean} success
22098      * set this after drop listener.. 
22099      */
22100     success : false,
22101     /**
22102      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22103      * if the drop point is valid for over/enter..
22104      */
22105     valid : false,
22106     // private
22107     isTarget : true,
22108
22109     // private
22110     isNotifyTarget : true,
22111     
22112     /**
22113      * @hide
22114      */
22115     notifyEnter : function(dd, e, data)
22116     {
22117         this.valid = true;
22118         this.fireEvent('enter', dd, e, data);
22119         if(this.overClass){
22120             this.el.addClass(this.overClass);
22121         }
22122         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22123             this.valid ? this.dropAllowed : this.dropNotAllowed
22124         );
22125     },
22126
22127     /**
22128      * @hide
22129      */
22130     notifyOver : function(dd, e, data)
22131     {
22132         this.valid = true;
22133         this.fireEvent('over', dd, e, data);
22134         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22135             this.valid ? this.dropAllowed : this.dropNotAllowed
22136         );
22137     },
22138
22139     /**
22140      * @hide
22141      */
22142     notifyOut : function(dd, e, data)
22143     {
22144         this.fireEvent('out', dd, e, data);
22145         if(this.overClass){
22146             this.el.removeClass(this.overClass);
22147         }
22148     },
22149
22150     /**
22151      * @hide
22152      */
22153     notifyDrop : function(dd, e, data)
22154     {
22155         this.success = false;
22156         this.fireEvent('drop', dd, e, data);
22157         return this.success;
22158     }
22159 });/*
22160  * Based on:
22161  * Ext JS Library 1.1.1
22162  * Copyright(c) 2006-2007, Ext JS, LLC.
22163  *
22164  * Originally Released Under LGPL - original licence link has changed is not relivant.
22165  *
22166  * Fork - LGPL
22167  * <script type="text/javascript">
22168  */
22169
22170
22171 /**
22172  * @class Roo.dd.DragZone
22173  * @extends Roo.dd.DragSource
22174  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22175  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22176  * @constructor
22177  * @param {String/HTMLElement/Element} el The container element
22178  * @param {Object} config
22179  */
22180 Roo.dd.DragZone = function(el, config){
22181     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22182     if(this.containerScroll){
22183         Roo.dd.ScrollManager.register(this.el);
22184     }
22185 };
22186
22187 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22188     /**
22189      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22190      * for auto scrolling during drag operations.
22191      */
22192     /**
22193      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22194      * method after a failed drop (defaults to "c3daf9" - light blue)
22195      */
22196
22197     /**
22198      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22199      * for a valid target to drag based on the mouse down. Override this method
22200      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22201      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22202      * @param {EventObject} e The mouse down event
22203      * @return {Object} The dragData
22204      */
22205     getDragData : function(e){
22206         return Roo.dd.Registry.getHandleFromEvent(e);
22207     },
22208     
22209     /**
22210      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22211      * this.dragData.ddel
22212      * @param {Number} x The x position of the click on the dragged object
22213      * @param {Number} y The y position of the click on the dragged object
22214      * @return {Boolean} true to continue the drag, false to cancel
22215      */
22216     onInitDrag : function(x, y){
22217         this.proxy.update(this.dragData.ddel.cloneNode(true));
22218         this.onStartDrag(x, y);
22219         return true;
22220     },
22221     
22222     /**
22223      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22224      */
22225     afterRepair : function(){
22226         if(Roo.enableFx){
22227             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22228         }
22229         this.dragging = false;
22230     },
22231
22232     /**
22233      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22234      * the XY of this.dragData.ddel
22235      * @param {EventObject} e The mouse up event
22236      * @return {Array} The xy location (e.g. [100, 200])
22237      */
22238     getRepairXY : function(e){
22239         return Roo.Element.fly(this.dragData.ddel).getXY();  
22240     }
22241 });/*
22242  * Based on:
22243  * Ext JS Library 1.1.1
22244  * Copyright(c) 2006-2007, Ext JS, LLC.
22245  *
22246  * Originally Released Under LGPL - original licence link has changed is not relivant.
22247  *
22248  * Fork - LGPL
22249  * <script type="text/javascript">
22250  */
22251 /**
22252  * @class Roo.dd.DropZone
22253  * @extends Roo.dd.DropTarget
22254  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22255  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22256  * @constructor
22257  * @param {String/HTMLElement/Element} el The container element
22258  * @param {Object} config
22259  */
22260 Roo.dd.DropZone = function(el, config){
22261     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22262 };
22263
22264 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22265     /**
22266      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22267      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22268      * provide your own custom lookup.
22269      * @param {Event} e The event
22270      * @return {Object} data The custom data
22271      */
22272     getTargetFromEvent : function(e){
22273         return Roo.dd.Registry.getTargetFromEvent(e);
22274     },
22275
22276     /**
22277      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22278      * that it has registered.  This method has no default implementation and should be overridden to provide
22279      * node-specific processing if necessary.
22280      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22281      * {@link #getTargetFromEvent} for this node)
22282      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22283      * @param {Event} e The event
22284      * @param {Object} data An object containing arbitrary data supplied by the drag source
22285      */
22286     onNodeEnter : function(n, dd, e, data){
22287         
22288     },
22289
22290     /**
22291      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22292      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22293      * overridden to provide the proper feedback.
22294      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22295      * {@link #getTargetFromEvent} for this node)
22296      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22297      * @param {Event} e The event
22298      * @param {Object} data An object containing arbitrary data supplied by the drag source
22299      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22300      * underlying {@link Roo.dd.StatusProxy} can be updated
22301      */
22302     onNodeOver : function(n, dd, e, data){
22303         return this.dropAllowed;
22304     },
22305
22306     /**
22307      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22308      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22309      * node-specific processing if necessary.
22310      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22311      * {@link #getTargetFromEvent} for this node)
22312      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22313      * @param {Event} e The event
22314      * @param {Object} data An object containing arbitrary data supplied by the drag source
22315      */
22316     onNodeOut : function(n, dd, e, data){
22317         
22318     },
22319
22320     /**
22321      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22322      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22323      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22324      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22325      * {@link #getTargetFromEvent} for this node)
22326      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22327      * @param {Event} e The event
22328      * @param {Object} data An object containing arbitrary data supplied by the drag source
22329      * @return {Boolean} True if the drop was valid, else false
22330      */
22331     onNodeDrop : function(n, dd, e, data){
22332         return false;
22333     },
22334
22335     /**
22336      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22337      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22338      * it should be overridden to provide the proper feedback if necessary.
22339      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22340      * @param {Event} e The event
22341      * @param {Object} data An object containing arbitrary data supplied by the drag source
22342      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22343      * underlying {@link Roo.dd.StatusProxy} can be updated
22344      */
22345     onContainerOver : function(dd, e, data){
22346         return this.dropNotAllowed;
22347     },
22348
22349     /**
22350      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22351      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22352      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22353      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22354      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22355      * @param {Event} e The event
22356      * @param {Object} data An object containing arbitrary data supplied by the drag source
22357      * @return {Boolean} True if the drop was valid, else false
22358      */
22359     onContainerDrop : function(dd, e, data){
22360         return false;
22361     },
22362
22363     /**
22364      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22365      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22366      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22367      * you should override this method and provide a custom implementation.
22368      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22369      * @param {Event} e The event
22370      * @param {Object} data An object containing arbitrary data supplied by the drag source
22371      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22372      * underlying {@link Roo.dd.StatusProxy} can be updated
22373      */
22374     notifyEnter : function(dd, e, data){
22375         return this.dropNotAllowed;
22376     },
22377
22378     /**
22379      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22380      * This method will be called on every mouse movement while the drag source is over the drop zone.
22381      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22382      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22383      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22384      * registered node, it will call {@link #onContainerOver}.
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     notifyOver : function(dd, e, data){
22392         var n = this.getTargetFromEvent(e);
22393         if(!n){ // not over valid drop target
22394             if(this.lastOverNode){
22395                 this.onNodeOut(this.lastOverNode, dd, e, data);
22396                 this.lastOverNode = null;
22397             }
22398             return this.onContainerOver(dd, e, data);
22399         }
22400         if(this.lastOverNode != n){
22401             if(this.lastOverNode){
22402                 this.onNodeOut(this.lastOverNode, dd, e, data);
22403             }
22404             this.onNodeEnter(n, dd, e, data);
22405             this.lastOverNode = n;
22406         }
22407         return this.onNodeOver(n, dd, e, data);
22408     },
22409
22410     /**
22411      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22412      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22413      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22414      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22415      * @param {Event} e The event
22416      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22417      */
22418     notifyOut : function(dd, e, data){
22419         if(this.lastOverNode){
22420             this.onNodeOut(this.lastOverNode, dd, e, data);
22421             this.lastOverNode = null;
22422         }
22423     },
22424
22425     /**
22426      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22427      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22428      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22429      * otherwise it will call {@link #onContainerDrop}.
22430      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22431      * @param {Event} e The event
22432      * @param {Object} data An object containing arbitrary data supplied by the drag source
22433      * @return {Boolean} True if the drop was valid, else false
22434      */
22435     notifyDrop : function(dd, e, data){
22436         if(this.lastOverNode){
22437             this.onNodeOut(this.lastOverNode, dd, e, data);
22438             this.lastOverNode = null;
22439         }
22440         var n = this.getTargetFromEvent(e);
22441         return n ?
22442             this.onNodeDrop(n, dd, e, data) :
22443             this.onContainerDrop(dd, e, data);
22444     },
22445
22446     // private
22447     triggerCacheRefresh : function(){
22448         Roo.dd.DDM.refreshCache(this.groups);
22449     }  
22450 });/*
22451  * Based on:
22452  * Ext JS Library 1.1.1
22453  * Copyright(c) 2006-2007, Ext JS, LLC.
22454  *
22455  * Originally Released Under LGPL - original licence link has changed is not relivant.
22456  *
22457  * Fork - LGPL
22458  * <script type="text/javascript">
22459  */
22460
22461
22462 /**
22463  * @class Roo.data.SortTypes
22464  * @singleton
22465  * Defines the default sorting (casting?) comparison functions used when sorting data.
22466  */
22467 Roo.data.SortTypes = {
22468     /**
22469      * Default sort that does nothing
22470      * @param {Mixed} s The value being converted
22471      * @return {Mixed} The comparison value
22472      */
22473     none : function(s){
22474         return s;
22475     },
22476     
22477     /**
22478      * The regular expression used to strip tags
22479      * @type {RegExp}
22480      * @property
22481      */
22482     stripTagsRE : /<\/?[^>]+>/gi,
22483     
22484     /**
22485      * Strips all HTML tags to sort on text only
22486      * @param {Mixed} s The value being converted
22487      * @return {String} The comparison value
22488      */
22489     asText : function(s){
22490         return String(s).replace(this.stripTagsRE, "");
22491     },
22492     
22493     /**
22494      * Strips all HTML tags to sort on text only - Case insensitive
22495      * @param {Mixed} s The value being converted
22496      * @return {String} The comparison value
22497      */
22498     asUCText : function(s){
22499         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22500     },
22501     
22502     /**
22503      * Case insensitive string
22504      * @param {Mixed} s The value being converted
22505      * @return {String} The comparison value
22506      */
22507     asUCString : function(s) {
22508         return String(s).toUpperCase();
22509     },
22510     
22511     /**
22512      * Date sorting
22513      * @param {Mixed} s The value being converted
22514      * @return {Number} The comparison value
22515      */
22516     asDate : function(s) {
22517         if(!s){
22518             return 0;
22519         }
22520         if(s instanceof Date){
22521             return s.getTime();
22522         }
22523         return Date.parse(String(s));
22524     },
22525     
22526     /**
22527      * Float sorting
22528      * @param {Mixed} s The value being converted
22529      * @return {Float} The comparison value
22530      */
22531     asFloat : function(s) {
22532         var val = parseFloat(String(s).replace(/,/g, ""));
22533         if(isNaN(val)) {
22534             val = 0;
22535         }
22536         return val;
22537     },
22538     
22539     /**
22540      * Integer sorting
22541      * @param {Mixed} s The value being converted
22542      * @return {Number} The comparison value
22543      */
22544     asInt : function(s) {
22545         var val = parseInt(String(s).replace(/,/g, ""));
22546         if(isNaN(val)) {
22547             val = 0;
22548         }
22549         return val;
22550     }
22551 };/*
22552  * Based on:
22553  * Ext JS Library 1.1.1
22554  * Copyright(c) 2006-2007, Ext JS, LLC.
22555  *
22556  * Originally Released Under LGPL - original licence link has changed is not relivant.
22557  *
22558  * Fork - LGPL
22559  * <script type="text/javascript">
22560  */
22561
22562 /**
22563 * @class Roo.data.Record
22564  * Instances of this class encapsulate both record <em>definition</em> information, and record
22565  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22566  * to access Records cached in an {@link Roo.data.Store} object.<br>
22567  * <p>
22568  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22569  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22570  * objects.<br>
22571  * <p>
22572  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22573  * @constructor
22574  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22575  * {@link #create}. The parameters are the same.
22576  * @param {Array} data An associative Array of data values keyed by the field name.
22577  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22578  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22579  * not specified an integer id is generated.
22580  */
22581 Roo.data.Record = function(data, id){
22582     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22583     this.data = data;
22584 };
22585
22586 /**
22587  * Generate a constructor for a specific record layout.
22588  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22589  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22590  * Each field definition object may contain the following properties: <ul>
22591  * <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,
22592  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22593  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22594  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22595  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22596  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22597  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22598  * this may be omitted.</p></li>
22599  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22600  * <ul><li>auto (Default, implies no conversion)</li>
22601  * <li>string</li>
22602  * <li>int</li>
22603  * <li>float</li>
22604  * <li>boolean</li>
22605  * <li>date</li></ul></p></li>
22606  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22607  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22608  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22609  * by the Reader into an object that will be stored in the Record. It is passed the
22610  * following parameters:<ul>
22611  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22612  * </ul></p></li>
22613  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22614  * </ul>
22615  * <br>usage:<br><pre><code>
22616 var TopicRecord = Roo.data.Record.create(
22617     {name: 'title', mapping: 'topic_title'},
22618     {name: 'author', mapping: 'username'},
22619     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22620     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22621     {name: 'lastPoster', mapping: 'user2'},
22622     {name: 'excerpt', mapping: 'post_text'}
22623 );
22624
22625 var myNewRecord = new TopicRecord({
22626     title: 'Do my job please',
22627     author: 'noobie',
22628     totalPosts: 1,
22629     lastPost: new Date(),
22630     lastPoster: 'Animal',
22631     excerpt: 'No way dude!'
22632 });
22633 myStore.add(myNewRecord);
22634 </code></pre>
22635  * @method create
22636  * @static
22637  */
22638 Roo.data.Record.create = function(o){
22639     var f = function(){
22640         f.superclass.constructor.apply(this, arguments);
22641     };
22642     Roo.extend(f, Roo.data.Record);
22643     var p = f.prototype;
22644     p.fields = new Roo.util.MixedCollection(false, function(field){
22645         return field.name;
22646     });
22647     for(var i = 0, len = o.length; i < len; i++){
22648         p.fields.add(new Roo.data.Field(o[i]));
22649     }
22650     f.getField = function(name){
22651         return p.fields.get(name);  
22652     };
22653     return f;
22654 };
22655
22656 Roo.data.Record.AUTO_ID = 1000;
22657 Roo.data.Record.EDIT = 'edit';
22658 Roo.data.Record.REJECT = 'reject';
22659 Roo.data.Record.COMMIT = 'commit';
22660
22661 Roo.data.Record.prototype = {
22662     /**
22663      * Readonly flag - true if this record has been modified.
22664      * @type Boolean
22665      */
22666     dirty : false,
22667     editing : false,
22668     error: null,
22669     modified: null,
22670
22671     // private
22672     join : function(store){
22673         this.store = store;
22674     },
22675
22676     /**
22677      * Set the named field to the specified value.
22678      * @param {String} name The name of the field to set.
22679      * @param {Object} value The value to set the field to.
22680      */
22681     set : function(name, value){
22682         if(this.data[name] == value){
22683             return;
22684         }
22685         this.dirty = true;
22686         if(!this.modified){
22687             this.modified = {};
22688         }
22689         if(typeof this.modified[name] == 'undefined'){
22690             this.modified[name] = this.data[name];
22691         }
22692         this.data[name] = value;
22693         if(!this.editing && this.store){
22694             this.store.afterEdit(this);
22695         }       
22696     },
22697
22698     /**
22699      * Get the value of the named field.
22700      * @param {String} name The name of the field to get the value of.
22701      * @return {Object} The value of the field.
22702      */
22703     get : function(name){
22704         return this.data[name]; 
22705     },
22706
22707     // private
22708     beginEdit : function(){
22709         this.editing = true;
22710         this.modified = {}; 
22711     },
22712
22713     // private
22714     cancelEdit : function(){
22715         this.editing = false;
22716         delete this.modified;
22717     },
22718
22719     // private
22720     endEdit : function(){
22721         this.editing = false;
22722         if(this.dirty && this.store){
22723             this.store.afterEdit(this);
22724         }
22725     },
22726
22727     /**
22728      * Usually called by the {@link Roo.data.Store} which owns the Record.
22729      * Rejects all changes made to the Record since either creation, or the last commit operation.
22730      * Modified fields are reverted to their original values.
22731      * <p>
22732      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22733      * of reject operations.
22734      */
22735     reject : function(){
22736         var m = this.modified;
22737         for(var n in m){
22738             if(typeof m[n] != "function"){
22739                 this.data[n] = m[n];
22740             }
22741         }
22742         this.dirty = false;
22743         delete this.modified;
22744         this.editing = false;
22745         if(this.store){
22746             this.store.afterReject(this);
22747         }
22748     },
22749
22750     /**
22751      * Usually called by the {@link Roo.data.Store} which owns the Record.
22752      * Commits all changes made to the Record since either creation, or the last commit operation.
22753      * <p>
22754      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22755      * of commit operations.
22756      */
22757     commit : function(){
22758         this.dirty = false;
22759         delete this.modified;
22760         this.editing = false;
22761         if(this.store){
22762             this.store.afterCommit(this);
22763         }
22764     },
22765
22766     // private
22767     hasError : function(){
22768         return this.error != null;
22769     },
22770
22771     // private
22772     clearError : function(){
22773         this.error = null;
22774     },
22775
22776     /**
22777      * Creates a copy of this record.
22778      * @param {String} id (optional) A new record id if you don't want to use this record's id
22779      * @return {Record}
22780      */
22781     copy : function(newId) {
22782         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22783     }
22784 };/*
22785  * Based on:
22786  * Ext JS Library 1.1.1
22787  * Copyright(c) 2006-2007, Ext JS, LLC.
22788  *
22789  * Originally Released Under LGPL - original licence link has changed is not relivant.
22790  *
22791  * Fork - LGPL
22792  * <script type="text/javascript">
22793  */
22794
22795
22796
22797 /**
22798  * @class Roo.data.Store
22799  * @extends Roo.util.Observable
22800  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22801  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22802  * <p>
22803  * 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
22804  * has no knowledge of the format of the data returned by the Proxy.<br>
22805  * <p>
22806  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22807  * instances from the data object. These records are cached and made available through accessor functions.
22808  * @constructor
22809  * Creates a new Store.
22810  * @param {Object} config A config object containing the objects needed for the Store to access data,
22811  * and read the data into Records.
22812  */
22813 Roo.data.Store = function(config){
22814     this.data = new Roo.util.MixedCollection(false);
22815     this.data.getKey = function(o){
22816         return o.id;
22817     };
22818     this.baseParams = {};
22819     // private
22820     this.paramNames = {
22821         "start" : "start",
22822         "limit" : "limit",
22823         "sort" : "sort",
22824         "dir" : "dir",
22825         "multisort" : "_multisort"
22826     };
22827
22828     if(config && config.data){
22829         this.inlineData = config.data;
22830         delete config.data;
22831     }
22832
22833     Roo.apply(this, config);
22834     
22835     if(this.reader){ // reader passed
22836         this.reader = Roo.factory(this.reader, Roo.data);
22837         this.reader.xmodule = this.xmodule || false;
22838         if(!this.recordType){
22839             this.recordType = this.reader.recordType;
22840         }
22841         if(this.reader.onMetaChange){
22842             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22843         }
22844     }
22845
22846     if(this.recordType){
22847         this.fields = this.recordType.prototype.fields;
22848     }
22849     this.modified = [];
22850
22851     this.addEvents({
22852         /**
22853          * @event datachanged
22854          * Fires when the data cache has changed, and a widget which is using this Store
22855          * as a Record cache should refresh its view.
22856          * @param {Store} this
22857          */
22858         datachanged : true,
22859         /**
22860          * @event metachange
22861          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22862          * @param {Store} this
22863          * @param {Object} meta The JSON metadata
22864          */
22865         metachange : true,
22866         /**
22867          * @event add
22868          * Fires when Records have been added to the Store
22869          * @param {Store} this
22870          * @param {Roo.data.Record[]} records The array of Records added
22871          * @param {Number} index The index at which the record(s) were added
22872          */
22873         add : true,
22874         /**
22875          * @event remove
22876          * Fires when a Record has been removed from the Store
22877          * @param {Store} this
22878          * @param {Roo.data.Record} record The Record that was removed
22879          * @param {Number} index The index at which the record was removed
22880          */
22881         remove : true,
22882         /**
22883          * @event update
22884          * Fires when a Record has been updated
22885          * @param {Store} this
22886          * @param {Roo.data.Record} record The Record that was updated
22887          * @param {String} operation The update operation being performed.  Value may be one of:
22888          * <pre><code>
22889  Roo.data.Record.EDIT
22890  Roo.data.Record.REJECT
22891  Roo.data.Record.COMMIT
22892          * </code></pre>
22893          */
22894         update : true,
22895         /**
22896          * @event clear
22897          * Fires when the data cache has been cleared.
22898          * @param {Store} this
22899          */
22900         clear : true,
22901         /**
22902          * @event beforeload
22903          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22904          * the load action will be canceled.
22905          * @param {Store} this
22906          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22907          */
22908         beforeload : true,
22909         /**
22910          * @event beforeloadadd
22911          * Fires after a new set of Records has been loaded.
22912          * @param {Store} this
22913          * @param {Roo.data.Record[]} records The Records that were loaded
22914          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22915          */
22916         beforeloadadd : true,
22917         /**
22918          * @event load
22919          * Fires after a new set of Records has been loaded, before they are added to the store.
22920          * @param {Store} this
22921          * @param {Roo.data.Record[]} records The Records that were loaded
22922          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22923          * @params {Object} return from reader
22924          */
22925         load : true,
22926         /**
22927          * @event loadexception
22928          * Fires if an exception occurs in the Proxy during loading.
22929          * Called with the signature of the Proxy's "loadexception" event.
22930          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22931          * 
22932          * @param {Proxy} 
22933          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22934          * @param {Object} load options 
22935          * @param {Object} jsonData from your request (normally this contains the Exception)
22936          */
22937         loadexception : true
22938     });
22939     
22940     if(this.proxy){
22941         this.proxy = Roo.factory(this.proxy, Roo.data);
22942         this.proxy.xmodule = this.xmodule || false;
22943         this.relayEvents(this.proxy,  ["loadexception"]);
22944     }
22945     this.sortToggle = {};
22946     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22947
22948     Roo.data.Store.superclass.constructor.call(this);
22949
22950     if(this.inlineData){
22951         this.loadData(this.inlineData);
22952         delete this.inlineData;
22953     }
22954 };
22955
22956 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22957      /**
22958     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22959     * without a remote query - used by combo/forms at present.
22960     */
22961     
22962     /**
22963     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22964     */
22965     /**
22966     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22967     */
22968     /**
22969     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22970     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22971     */
22972     /**
22973     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22974     * on any HTTP request
22975     */
22976     /**
22977     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22978     */
22979     /**
22980     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22981     */
22982     multiSort: false,
22983     /**
22984     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22985     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22986     */
22987     remoteSort : false,
22988
22989     /**
22990     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22991      * loaded or when a record is removed. (defaults to false).
22992     */
22993     pruneModifiedRecords : false,
22994
22995     // private
22996     lastOptions : null,
22997
22998     /**
22999      * Add Records to the Store and fires the add event.
23000      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23001      */
23002     add : function(records){
23003         records = [].concat(records);
23004         for(var i = 0, len = records.length; i < len; i++){
23005             records[i].join(this);
23006         }
23007         var index = this.data.length;
23008         this.data.addAll(records);
23009         this.fireEvent("add", this, records, index);
23010     },
23011
23012     /**
23013      * Remove a Record from the Store and fires the remove event.
23014      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23015      */
23016     remove : function(record){
23017         var index = this.data.indexOf(record);
23018         this.data.removeAt(index);
23019  
23020         if(this.pruneModifiedRecords){
23021             this.modified.remove(record);
23022         }
23023         this.fireEvent("remove", this, record, index);
23024     },
23025
23026     /**
23027      * Remove all Records from the Store and fires the clear event.
23028      */
23029     removeAll : function(){
23030         this.data.clear();
23031         if(this.pruneModifiedRecords){
23032             this.modified = [];
23033         }
23034         this.fireEvent("clear", this);
23035     },
23036
23037     /**
23038      * Inserts Records to the Store at the given index and fires the add event.
23039      * @param {Number} index The start index at which to insert the passed Records.
23040      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23041      */
23042     insert : function(index, records){
23043         records = [].concat(records);
23044         for(var i = 0, len = records.length; i < len; i++){
23045             this.data.insert(index, records[i]);
23046             records[i].join(this);
23047         }
23048         this.fireEvent("add", this, records, index);
23049     },
23050
23051     /**
23052      * Get the index within the cache of the passed Record.
23053      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23054      * @return {Number} The index of the passed Record. Returns -1 if not found.
23055      */
23056     indexOf : function(record){
23057         return this.data.indexOf(record);
23058     },
23059
23060     /**
23061      * Get the index within the cache of the Record with the passed id.
23062      * @param {String} id The id of the Record to find.
23063      * @return {Number} The index of the Record. Returns -1 if not found.
23064      */
23065     indexOfId : function(id){
23066         return this.data.indexOfKey(id);
23067     },
23068
23069     /**
23070      * Get the Record with the specified id.
23071      * @param {String} id The id of the Record to find.
23072      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23073      */
23074     getById : function(id){
23075         return this.data.key(id);
23076     },
23077
23078     /**
23079      * Get the Record at the specified index.
23080      * @param {Number} index The index of the Record to find.
23081      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23082      */
23083     getAt : function(index){
23084         return this.data.itemAt(index);
23085     },
23086
23087     /**
23088      * Returns a range of Records between specified indices.
23089      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23090      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23091      * @return {Roo.data.Record[]} An array of Records
23092      */
23093     getRange : function(start, end){
23094         return this.data.getRange(start, end);
23095     },
23096
23097     // private
23098     storeOptions : function(o){
23099         o = Roo.apply({}, o);
23100         delete o.callback;
23101         delete o.scope;
23102         this.lastOptions = o;
23103     },
23104
23105     /**
23106      * Loads the Record cache from the configured Proxy using the configured Reader.
23107      * <p>
23108      * If using remote paging, then the first load call must specify the <em>start</em>
23109      * and <em>limit</em> properties in the options.params property to establish the initial
23110      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23111      * <p>
23112      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23113      * and this call will return before the new data has been loaded. Perform any post-processing
23114      * in a callback function, or in a "load" event handler.</strong>
23115      * <p>
23116      * @param {Object} options An object containing properties which control loading options:<ul>
23117      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23118      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23119      * passed the following arguments:<ul>
23120      * <li>r : Roo.data.Record[]</li>
23121      * <li>options: Options object from the load call</li>
23122      * <li>success: Boolean success indicator</li></ul></li>
23123      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23124      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23125      * </ul>
23126      */
23127     load : function(options){
23128         options = options || {};
23129         if(this.fireEvent("beforeload", this, options) !== false){
23130             this.storeOptions(options);
23131             var p = Roo.apply(options.params || {}, this.baseParams);
23132             // if meta was not loaded from remote source.. try requesting it.
23133             if (!this.reader.metaFromRemote) {
23134                 p._requestMeta = 1;
23135             }
23136             if(this.sortInfo && this.remoteSort){
23137                 var pn = this.paramNames;
23138                 p[pn["sort"]] = this.sortInfo.field;
23139                 p[pn["dir"]] = this.sortInfo.direction;
23140             }
23141             if (this.multiSort) {
23142                 var pn = this.paramNames;
23143                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23144             }
23145             
23146             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23147         }
23148     },
23149
23150     /**
23151      * Reloads the Record cache from the configured Proxy using the configured Reader and
23152      * the options from the last load operation performed.
23153      * @param {Object} options (optional) An object containing properties which may override the options
23154      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23155      * the most recently used options are reused).
23156      */
23157     reload : function(options){
23158         this.load(Roo.applyIf(options||{}, this.lastOptions));
23159     },
23160
23161     // private
23162     // Called as a callback by the Reader during a load operation.
23163     loadRecords : function(o, options, success){
23164         if(!o || success === false){
23165             if(success !== false){
23166                 this.fireEvent("load", this, [], options, o);
23167             }
23168             if(options.callback){
23169                 options.callback.call(options.scope || this, [], options, false);
23170             }
23171             return;
23172         }
23173         // if data returned failure - throw an exception.
23174         if (o.success === false) {
23175             // show a message if no listener is registered.
23176             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23177                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23178             }
23179             // loadmask wil be hooked into this..
23180             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23181             return;
23182         }
23183         var r = o.records, t = o.totalRecords || r.length;
23184         
23185         this.fireEvent("beforeloadadd", this, r, options, o);
23186         
23187         if(!options || options.add !== true){
23188             if(this.pruneModifiedRecords){
23189                 this.modified = [];
23190             }
23191             for(var i = 0, len = r.length; i < len; i++){
23192                 r[i].join(this);
23193             }
23194             if(this.snapshot){
23195                 this.data = this.snapshot;
23196                 delete this.snapshot;
23197             }
23198             this.data.clear();
23199             this.data.addAll(r);
23200             this.totalLength = t;
23201             this.applySort();
23202             this.fireEvent("datachanged", this);
23203         }else{
23204             this.totalLength = Math.max(t, this.data.length+r.length);
23205             this.add(r);
23206         }
23207         
23208         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23209                 
23210             var e = new Roo.data.Record({});
23211
23212             e.set(this.parent.displayField, this.parent.emptyTitle);
23213             e.set(this.parent.valueField, '');
23214
23215             this.insert(0, e);
23216         }
23217             
23218         this.fireEvent("load", this, r, options, o);
23219         if(options.callback){
23220             options.callback.call(options.scope || this, r, options, true);
23221         }
23222     },
23223
23224
23225     /**
23226      * Loads data from a passed data block. A Reader which understands the format of the data
23227      * must have been configured in the constructor.
23228      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23229      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23230      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23231      */
23232     loadData : function(o, append){
23233         var r = this.reader.readRecords(o);
23234         this.loadRecords(r, {add: append}, true);
23235     },
23236
23237     /**
23238      * Gets the number of cached records.
23239      * <p>
23240      * <em>If using paging, this may not be the total size of the dataset. If the data object
23241      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23242      * the data set size</em>
23243      */
23244     getCount : function(){
23245         return this.data.length || 0;
23246     },
23247
23248     /**
23249      * Gets the total number of records in the dataset as returned by the server.
23250      * <p>
23251      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23252      * the dataset size</em>
23253      */
23254     getTotalCount : function(){
23255         return this.totalLength || 0;
23256     },
23257
23258     /**
23259      * Returns the sort state of the Store as an object with two properties:
23260      * <pre><code>
23261  field {String} The name of the field by which the Records are sorted
23262  direction {String} The sort order, "ASC" or "DESC"
23263      * </code></pre>
23264      */
23265     getSortState : function(){
23266         return this.sortInfo;
23267     },
23268
23269     // private
23270     applySort : function(){
23271         if(this.sortInfo && !this.remoteSort){
23272             var s = this.sortInfo, f = s.field;
23273             var st = this.fields.get(f).sortType;
23274             var fn = function(r1, r2){
23275                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23276                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23277             };
23278             this.data.sort(s.direction, fn);
23279             if(this.snapshot && this.snapshot != this.data){
23280                 this.snapshot.sort(s.direction, fn);
23281             }
23282         }
23283     },
23284
23285     /**
23286      * Sets the default sort column and order to be used by the next load operation.
23287      * @param {String} fieldName The name of the field to sort by.
23288      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23289      */
23290     setDefaultSort : function(field, dir){
23291         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23292     },
23293
23294     /**
23295      * Sort the Records.
23296      * If remote sorting is used, the sort is performed on the server, and the cache is
23297      * reloaded. If local sorting is used, the cache is sorted internally.
23298      * @param {String} fieldName The name of the field to sort by.
23299      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23300      */
23301     sort : function(fieldName, dir){
23302         var f = this.fields.get(fieldName);
23303         if(!dir){
23304             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23305             
23306             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23307                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23308             }else{
23309                 dir = f.sortDir;
23310             }
23311         }
23312         this.sortToggle[f.name] = dir;
23313         this.sortInfo = {field: f.name, direction: dir};
23314         if(!this.remoteSort){
23315             this.applySort();
23316             this.fireEvent("datachanged", this);
23317         }else{
23318             this.load(this.lastOptions);
23319         }
23320     },
23321
23322     /**
23323      * Calls the specified function for each of the Records in the cache.
23324      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23325      * Returning <em>false</em> aborts and exits the iteration.
23326      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23327      */
23328     each : function(fn, scope){
23329         this.data.each(fn, scope);
23330     },
23331
23332     /**
23333      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23334      * (e.g., during paging).
23335      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23336      */
23337     getModifiedRecords : function(){
23338         return this.modified;
23339     },
23340
23341     // private
23342     createFilterFn : function(property, value, anyMatch){
23343         if(!value.exec){ // not a regex
23344             value = String(value);
23345             if(value.length == 0){
23346                 return false;
23347             }
23348             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23349         }
23350         return function(r){
23351             return value.test(r.data[property]);
23352         };
23353     },
23354
23355     /**
23356      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23357      * @param {String} property A field on your records
23358      * @param {Number} start The record index to start at (defaults to 0)
23359      * @param {Number} end The last record index to include (defaults to length - 1)
23360      * @return {Number} The sum
23361      */
23362     sum : function(property, start, end){
23363         var rs = this.data.items, v = 0;
23364         start = start || 0;
23365         end = (end || end === 0) ? end : rs.length-1;
23366
23367         for(var i = start; i <= end; i++){
23368             v += (rs[i].data[property] || 0);
23369         }
23370         return v;
23371     },
23372
23373     /**
23374      * Filter the records by a specified property.
23375      * @param {String} field A field on your records
23376      * @param {String/RegExp} value Either a string that the field
23377      * should start with or a RegExp to test against the field
23378      * @param {Boolean} anyMatch True to match any part not just the beginning
23379      */
23380     filter : function(property, value, anyMatch){
23381         var fn = this.createFilterFn(property, value, anyMatch);
23382         return fn ? this.filterBy(fn) : this.clearFilter();
23383     },
23384
23385     /**
23386      * Filter by a function. The specified function will be called with each
23387      * record in this data source. If the function returns true the record is included,
23388      * otherwise it is filtered.
23389      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23390      * @param {Object} scope (optional) The scope of the function (defaults to this)
23391      */
23392     filterBy : function(fn, scope){
23393         this.snapshot = this.snapshot || this.data;
23394         this.data = this.queryBy(fn, scope||this);
23395         this.fireEvent("datachanged", this);
23396     },
23397
23398     /**
23399      * Query the records by a specified property.
23400      * @param {String} field A field on your records
23401      * @param {String/RegExp} value Either a string that the field
23402      * should start with or a RegExp to test against the field
23403      * @param {Boolean} anyMatch True to match any part not just the beginning
23404      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23405      */
23406     query : function(property, value, anyMatch){
23407         var fn = this.createFilterFn(property, value, anyMatch);
23408         return fn ? this.queryBy(fn) : this.data.clone();
23409     },
23410
23411     /**
23412      * Query by a function. The specified function will be called with each
23413      * record in this data source. If the function returns true the record is included
23414      * in the results.
23415      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23416      * @param {Object} scope (optional) The scope of the function (defaults to this)
23417       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23418      **/
23419     queryBy : function(fn, scope){
23420         var data = this.snapshot || this.data;
23421         return data.filterBy(fn, scope||this);
23422     },
23423
23424     /**
23425      * Collects unique values for a particular dataIndex from this store.
23426      * @param {String} dataIndex The property to collect
23427      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23428      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23429      * @return {Array} An array of the unique values
23430      **/
23431     collect : function(dataIndex, allowNull, bypassFilter){
23432         var d = (bypassFilter === true && this.snapshot) ?
23433                 this.snapshot.items : this.data.items;
23434         var v, sv, r = [], l = {};
23435         for(var i = 0, len = d.length; i < len; i++){
23436             v = d[i].data[dataIndex];
23437             sv = String(v);
23438             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23439                 l[sv] = true;
23440                 r[r.length] = v;
23441             }
23442         }
23443         return r;
23444     },
23445
23446     /**
23447      * Revert to a view of the Record cache with no filtering applied.
23448      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23449      */
23450     clearFilter : function(suppressEvent){
23451         if(this.snapshot && this.snapshot != this.data){
23452             this.data = this.snapshot;
23453             delete this.snapshot;
23454             if(suppressEvent !== true){
23455                 this.fireEvent("datachanged", this);
23456             }
23457         }
23458     },
23459
23460     // private
23461     afterEdit : function(record){
23462         if(this.modified.indexOf(record) == -1){
23463             this.modified.push(record);
23464         }
23465         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23466     },
23467     
23468     // private
23469     afterReject : function(record){
23470         this.modified.remove(record);
23471         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23472     },
23473
23474     // private
23475     afterCommit : function(record){
23476         this.modified.remove(record);
23477         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23478     },
23479
23480     /**
23481      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23482      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23483      */
23484     commitChanges : function(){
23485         var m = this.modified.slice(0);
23486         this.modified = [];
23487         for(var i = 0, len = m.length; i < len; i++){
23488             m[i].commit();
23489         }
23490     },
23491
23492     /**
23493      * Cancel outstanding changes on all changed records.
23494      */
23495     rejectChanges : function(){
23496         var m = this.modified.slice(0);
23497         this.modified = [];
23498         for(var i = 0, len = m.length; i < len; i++){
23499             m[i].reject();
23500         }
23501     },
23502
23503     onMetaChange : function(meta, rtype, o){
23504         this.recordType = rtype;
23505         this.fields = rtype.prototype.fields;
23506         delete this.snapshot;
23507         this.sortInfo = meta.sortInfo || this.sortInfo;
23508         this.modified = [];
23509         this.fireEvent('metachange', this, this.reader.meta);
23510     },
23511     
23512     moveIndex : function(data, type)
23513     {
23514         var index = this.indexOf(data);
23515         
23516         var newIndex = index + type;
23517         
23518         this.remove(data);
23519         
23520         this.insert(newIndex, data);
23521         
23522     }
23523 });/*
23524  * Based on:
23525  * Ext JS Library 1.1.1
23526  * Copyright(c) 2006-2007, Ext JS, LLC.
23527  *
23528  * Originally Released Under LGPL - original licence link has changed is not relivant.
23529  *
23530  * Fork - LGPL
23531  * <script type="text/javascript">
23532  */
23533
23534 /**
23535  * @class Roo.data.SimpleStore
23536  * @extends Roo.data.Store
23537  * Small helper class to make creating Stores from Array data easier.
23538  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23539  * @cfg {Array} fields An array of field definition objects, or field name strings.
23540  * @cfg {Array} data The multi-dimensional array of data
23541  * @constructor
23542  * @param {Object} config
23543  */
23544 Roo.data.SimpleStore = function(config){
23545     Roo.data.SimpleStore.superclass.constructor.call(this, {
23546         isLocal : true,
23547         reader: new Roo.data.ArrayReader({
23548                 id: config.id
23549             },
23550             Roo.data.Record.create(config.fields)
23551         ),
23552         proxy : new Roo.data.MemoryProxy(config.data)
23553     });
23554     this.load();
23555 };
23556 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23557  * Based on:
23558  * Ext JS Library 1.1.1
23559  * Copyright(c) 2006-2007, Ext JS, LLC.
23560  *
23561  * Originally Released Under LGPL - original licence link has changed is not relivant.
23562  *
23563  * Fork - LGPL
23564  * <script type="text/javascript">
23565  */
23566
23567 /**
23568 /**
23569  * @extends Roo.data.Store
23570  * @class Roo.data.JsonStore
23571  * Small helper class to make creating Stores for JSON data easier. <br/>
23572 <pre><code>
23573 var store = new Roo.data.JsonStore({
23574     url: 'get-images.php',
23575     root: 'images',
23576     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23577 });
23578 </code></pre>
23579  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23580  * JsonReader and HttpProxy (unless inline data is provided).</b>
23581  * @cfg {Array} fields An array of field definition objects, or field name strings.
23582  * @constructor
23583  * @param {Object} config
23584  */
23585 Roo.data.JsonStore = function(c){
23586     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23587         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23588         reader: new Roo.data.JsonReader(c, c.fields)
23589     }));
23590 };
23591 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23592  * Based on:
23593  * Ext JS Library 1.1.1
23594  * Copyright(c) 2006-2007, Ext JS, LLC.
23595  *
23596  * Originally Released Under LGPL - original licence link has changed is not relivant.
23597  *
23598  * Fork - LGPL
23599  * <script type="text/javascript">
23600  */
23601
23602  
23603 Roo.data.Field = function(config){
23604     if(typeof config == "string"){
23605         config = {name: config};
23606     }
23607     Roo.apply(this, config);
23608     
23609     if(!this.type){
23610         this.type = "auto";
23611     }
23612     
23613     var st = Roo.data.SortTypes;
23614     // named sortTypes are supported, here we look them up
23615     if(typeof this.sortType == "string"){
23616         this.sortType = st[this.sortType];
23617     }
23618     
23619     // set default sortType for strings and dates
23620     if(!this.sortType){
23621         switch(this.type){
23622             case "string":
23623                 this.sortType = st.asUCString;
23624                 break;
23625             case "date":
23626                 this.sortType = st.asDate;
23627                 break;
23628             default:
23629                 this.sortType = st.none;
23630         }
23631     }
23632
23633     // define once
23634     var stripRe = /[\$,%]/g;
23635
23636     // prebuilt conversion function for this field, instead of
23637     // switching every time we're reading a value
23638     if(!this.convert){
23639         var cv, dateFormat = this.dateFormat;
23640         switch(this.type){
23641             case "":
23642             case "auto":
23643             case undefined:
23644                 cv = function(v){ return v; };
23645                 break;
23646             case "string":
23647                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23648                 break;
23649             case "int":
23650                 cv = function(v){
23651                     return v !== undefined && v !== null && v !== '' ?
23652                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23653                     };
23654                 break;
23655             case "float":
23656                 cv = function(v){
23657                     return v !== undefined && v !== null && v !== '' ?
23658                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23659                     };
23660                 break;
23661             case "bool":
23662             case "boolean":
23663                 cv = function(v){ return v === true || v === "true" || v == 1; };
23664                 break;
23665             case "date":
23666                 cv = function(v){
23667                     if(!v){
23668                         return '';
23669                     }
23670                     if(v instanceof Date){
23671                         return v;
23672                     }
23673                     if(dateFormat){
23674                         if(dateFormat == "timestamp"){
23675                             return new Date(v*1000);
23676                         }
23677                         return Date.parseDate(v, dateFormat);
23678                     }
23679                     var parsed = Date.parse(v);
23680                     return parsed ? new Date(parsed) : null;
23681                 };
23682              break;
23683             
23684         }
23685         this.convert = cv;
23686     }
23687 };
23688
23689 Roo.data.Field.prototype = {
23690     dateFormat: null,
23691     defaultValue: "",
23692     mapping: null,
23693     sortType : null,
23694     sortDir : "ASC"
23695 };/*
23696  * Based on:
23697  * Ext JS Library 1.1.1
23698  * Copyright(c) 2006-2007, Ext JS, LLC.
23699  *
23700  * Originally Released Under LGPL - original licence link has changed is not relivant.
23701  *
23702  * Fork - LGPL
23703  * <script type="text/javascript">
23704  */
23705  
23706 // Base class for reading structured data from a data source.  This class is intended to be
23707 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23708
23709 /**
23710  * @class Roo.data.DataReader
23711  * Base class for reading structured data from a data source.  This class is intended to be
23712  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23713  */
23714
23715 Roo.data.DataReader = function(meta, recordType){
23716     
23717     this.meta = meta;
23718     
23719     this.recordType = recordType instanceof Array ? 
23720         Roo.data.Record.create(recordType) : recordType;
23721 };
23722
23723 Roo.data.DataReader.prototype = {
23724      /**
23725      * Create an empty record
23726      * @param {Object} data (optional) - overlay some values
23727      * @return {Roo.data.Record} record created.
23728      */
23729     newRow :  function(d) {
23730         var da =  {};
23731         this.recordType.prototype.fields.each(function(c) {
23732             switch( c.type) {
23733                 case 'int' : da[c.name] = 0; break;
23734                 case 'date' : da[c.name] = new Date(); break;
23735                 case 'float' : da[c.name] = 0.0; break;
23736                 case 'boolean' : da[c.name] = false; break;
23737                 default : da[c.name] = ""; break;
23738             }
23739             
23740         });
23741         return new this.recordType(Roo.apply(da, d));
23742     }
23743     
23744 };/*
23745  * Based on:
23746  * Ext JS Library 1.1.1
23747  * Copyright(c) 2006-2007, Ext JS, LLC.
23748  *
23749  * Originally Released Under LGPL - original licence link has changed is not relivant.
23750  *
23751  * Fork - LGPL
23752  * <script type="text/javascript">
23753  */
23754
23755 /**
23756  * @class Roo.data.DataProxy
23757  * @extends Roo.data.Observable
23758  * This class is an abstract base class for implementations which provide retrieval of
23759  * unformatted data objects.<br>
23760  * <p>
23761  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23762  * (of the appropriate type which knows how to parse the data object) to provide a block of
23763  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23764  * <p>
23765  * Custom implementations must implement the load method as described in
23766  * {@link Roo.data.HttpProxy#load}.
23767  */
23768 Roo.data.DataProxy = function(){
23769     this.addEvents({
23770         /**
23771          * @event beforeload
23772          * Fires before a network request is made to retrieve a data object.
23773          * @param {Object} This DataProxy object.
23774          * @param {Object} params The params parameter to the load function.
23775          */
23776         beforeload : true,
23777         /**
23778          * @event load
23779          * Fires before the load method's callback is called.
23780          * @param {Object} This DataProxy object.
23781          * @param {Object} o The data object.
23782          * @param {Object} arg The callback argument object passed to the load function.
23783          */
23784         load : true,
23785         /**
23786          * @event loadexception
23787          * Fires if an Exception occurs during data retrieval.
23788          * @param {Object} This DataProxy object.
23789          * @param {Object} o The data object.
23790          * @param {Object} arg The callback argument object passed to the load function.
23791          * @param {Object} e The Exception.
23792          */
23793         loadexception : true
23794     });
23795     Roo.data.DataProxy.superclass.constructor.call(this);
23796 };
23797
23798 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23799
23800     /**
23801      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23802      */
23803 /*
23804  * Based on:
23805  * Ext JS Library 1.1.1
23806  * Copyright(c) 2006-2007, Ext JS, LLC.
23807  *
23808  * Originally Released Under LGPL - original licence link has changed is not relivant.
23809  *
23810  * Fork - LGPL
23811  * <script type="text/javascript">
23812  */
23813 /**
23814  * @class Roo.data.MemoryProxy
23815  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23816  * to the Reader when its load method is called.
23817  * @constructor
23818  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23819  */
23820 Roo.data.MemoryProxy = function(data){
23821     if (data.data) {
23822         data = data.data;
23823     }
23824     Roo.data.MemoryProxy.superclass.constructor.call(this);
23825     this.data = data;
23826 };
23827
23828 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23829     
23830     /**
23831      * Load data from the requested source (in this case an in-memory
23832      * data object passed to the constructor), read the data object into
23833      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23834      * process that block using the passed callback.
23835      * @param {Object} params This parameter is not used by the MemoryProxy class.
23836      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23837      * object into a block of Roo.data.Records.
23838      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23839      * The function must be passed <ul>
23840      * <li>The Record block object</li>
23841      * <li>The "arg" argument from the load function</li>
23842      * <li>A boolean success indicator</li>
23843      * </ul>
23844      * @param {Object} scope The scope in which to call the callback
23845      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23846      */
23847     load : function(params, reader, callback, scope, arg){
23848         params = params || {};
23849         var result;
23850         try {
23851             result = reader.readRecords(this.data);
23852         }catch(e){
23853             this.fireEvent("loadexception", this, arg, null, e);
23854             callback.call(scope, null, arg, false);
23855             return;
23856         }
23857         callback.call(scope, result, arg, true);
23858     },
23859     
23860     // private
23861     update : function(params, records){
23862         
23863     }
23864 });/*
23865  * Based on:
23866  * Ext JS Library 1.1.1
23867  * Copyright(c) 2006-2007, Ext JS, LLC.
23868  *
23869  * Originally Released Under LGPL - original licence link has changed is not relivant.
23870  *
23871  * Fork - LGPL
23872  * <script type="text/javascript">
23873  */
23874 /**
23875  * @class Roo.data.HttpProxy
23876  * @extends Roo.data.DataProxy
23877  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23878  * configured to reference a certain URL.<br><br>
23879  * <p>
23880  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23881  * from which the running page was served.<br><br>
23882  * <p>
23883  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23884  * <p>
23885  * Be aware that to enable the browser to parse an XML document, the server must set
23886  * the Content-Type header in the HTTP response to "text/xml".
23887  * @constructor
23888  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23889  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23890  * will be used to make the request.
23891  */
23892 Roo.data.HttpProxy = function(conn){
23893     Roo.data.HttpProxy.superclass.constructor.call(this);
23894     // is conn a conn config or a real conn?
23895     this.conn = conn;
23896     this.useAjax = !conn || !conn.events;
23897   
23898 };
23899
23900 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23901     // thse are take from connection...
23902     
23903     /**
23904      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23905      */
23906     /**
23907      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23908      * extra parameters to each request made by this object. (defaults to undefined)
23909      */
23910     /**
23911      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23912      *  to each request made by this object. (defaults to undefined)
23913      */
23914     /**
23915      * @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)
23916      */
23917     /**
23918      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23919      */
23920      /**
23921      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23922      * @type Boolean
23923      */
23924   
23925
23926     /**
23927      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23928      * @type Boolean
23929      */
23930     /**
23931      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23932      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23933      * a finer-grained basis than the DataProxy events.
23934      */
23935     getConnection : function(){
23936         return this.useAjax ? Roo.Ajax : this.conn;
23937     },
23938
23939     /**
23940      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23941      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23942      * process that block using the passed callback.
23943      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23944      * for the request to the remote server.
23945      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23946      * object into a block of Roo.data.Records.
23947      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23948      * The function must be passed <ul>
23949      * <li>The Record block object</li>
23950      * <li>The "arg" argument from the load function</li>
23951      * <li>A boolean success indicator</li>
23952      * </ul>
23953      * @param {Object} scope The scope in which to call the callback
23954      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23955      */
23956     load : function(params, reader, callback, scope, arg){
23957         if(this.fireEvent("beforeload", this, params) !== false){
23958             var  o = {
23959                 params : params || {},
23960                 request: {
23961                     callback : callback,
23962                     scope : scope,
23963                     arg : arg
23964                 },
23965                 reader: reader,
23966                 callback : this.loadResponse,
23967                 scope: this
23968             };
23969             if(this.useAjax){
23970                 Roo.applyIf(o, this.conn);
23971                 if(this.activeRequest){
23972                     Roo.Ajax.abort(this.activeRequest);
23973                 }
23974                 this.activeRequest = Roo.Ajax.request(o);
23975             }else{
23976                 this.conn.request(o);
23977             }
23978         }else{
23979             callback.call(scope||this, null, arg, false);
23980         }
23981     },
23982
23983     // private
23984     loadResponse : function(o, success, response){
23985         delete this.activeRequest;
23986         if(!success){
23987             this.fireEvent("loadexception", this, o, response);
23988             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23989             return;
23990         }
23991         var result;
23992         try {
23993             result = o.reader.read(response);
23994         }catch(e){
23995             this.fireEvent("loadexception", this, o, response, e);
23996             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23997             return;
23998         }
23999         
24000         this.fireEvent("load", this, o, o.request.arg);
24001         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24002     },
24003
24004     // private
24005     update : function(dataSet){
24006
24007     },
24008
24009     // private
24010     updateResponse : function(dataSet){
24011
24012     }
24013 });/*
24014  * Based on:
24015  * Ext JS Library 1.1.1
24016  * Copyright(c) 2006-2007, Ext JS, LLC.
24017  *
24018  * Originally Released Under LGPL - original licence link has changed is not relivant.
24019  *
24020  * Fork - LGPL
24021  * <script type="text/javascript">
24022  */
24023
24024 /**
24025  * @class Roo.data.ScriptTagProxy
24026  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24027  * other than the originating domain of the running page.<br><br>
24028  * <p>
24029  * <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
24030  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24031  * <p>
24032  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24033  * source code that is used as the source inside a &lt;script> tag.<br><br>
24034  * <p>
24035  * In order for the browser to process the returned data, the server must wrap the data object
24036  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24037  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24038  * depending on whether the callback name was passed:
24039  * <p>
24040  * <pre><code>
24041 boolean scriptTag = false;
24042 String cb = request.getParameter("callback");
24043 if (cb != null) {
24044     scriptTag = true;
24045     response.setContentType("text/javascript");
24046 } else {
24047     response.setContentType("application/x-json");
24048 }
24049 Writer out = response.getWriter();
24050 if (scriptTag) {
24051     out.write(cb + "(");
24052 }
24053 out.print(dataBlock.toJsonString());
24054 if (scriptTag) {
24055     out.write(");");
24056 }
24057 </pre></code>
24058  *
24059  * @constructor
24060  * @param {Object} config A configuration object.
24061  */
24062 Roo.data.ScriptTagProxy = function(config){
24063     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24064     Roo.apply(this, config);
24065     this.head = document.getElementsByTagName("head")[0];
24066 };
24067
24068 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24069
24070 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24071     /**
24072      * @cfg {String} url The URL from which to request the data object.
24073      */
24074     /**
24075      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24076      */
24077     timeout : 30000,
24078     /**
24079      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24080      * the server the name of the callback function set up by the load call to process the returned data object.
24081      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24082      * javascript output which calls this named function passing the data object as its only parameter.
24083      */
24084     callbackParam : "callback",
24085     /**
24086      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24087      * name to the request.
24088      */
24089     nocache : true,
24090
24091     /**
24092      * Load data from the configured URL, read the data object into
24093      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24094      * process that block using the passed callback.
24095      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24096      * for the request to the remote server.
24097      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24098      * object into a block of Roo.data.Records.
24099      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24100      * The function must be passed <ul>
24101      * <li>The Record block object</li>
24102      * <li>The "arg" argument from the load function</li>
24103      * <li>A boolean success indicator</li>
24104      * </ul>
24105      * @param {Object} scope The scope in which to call the callback
24106      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24107      */
24108     load : function(params, reader, callback, scope, arg){
24109         if(this.fireEvent("beforeload", this, params) !== false){
24110
24111             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24112
24113             var url = this.url;
24114             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24115             if(this.nocache){
24116                 url += "&_dc=" + (new Date().getTime());
24117             }
24118             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24119             var trans = {
24120                 id : transId,
24121                 cb : "stcCallback"+transId,
24122                 scriptId : "stcScript"+transId,
24123                 params : params,
24124                 arg : arg,
24125                 url : url,
24126                 callback : callback,
24127                 scope : scope,
24128                 reader : reader
24129             };
24130             var conn = this;
24131
24132             window[trans.cb] = function(o){
24133                 conn.handleResponse(o, trans);
24134             };
24135
24136             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24137
24138             if(this.autoAbort !== false){
24139                 this.abort();
24140             }
24141
24142             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24143
24144             var script = document.createElement("script");
24145             script.setAttribute("src", url);
24146             script.setAttribute("type", "text/javascript");
24147             script.setAttribute("id", trans.scriptId);
24148             this.head.appendChild(script);
24149
24150             this.trans = trans;
24151         }else{
24152             callback.call(scope||this, null, arg, false);
24153         }
24154     },
24155
24156     // private
24157     isLoading : function(){
24158         return this.trans ? true : false;
24159     },
24160
24161     /**
24162      * Abort the current server request.
24163      */
24164     abort : function(){
24165         if(this.isLoading()){
24166             this.destroyTrans(this.trans);
24167         }
24168     },
24169
24170     // private
24171     destroyTrans : function(trans, isLoaded){
24172         this.head.removeChild(document.getElementById(trans.scriptId));
24173         clearTimeout(trans.timeoutId);
24174         if(isLoaded){
24175             window[trans.cb] = undefined;
24176             try{
24177                 delete window[trans.cb];
24178             }catch(e){}
24179         }else{
24180             // if hasn't been loaded, wait for load to remove it to prevent script error
24181             window[trans.cb] = function(){
24182                 window[trans.cb] = undefined;
24183                 try{
24184                     delete window[trans.cb];
24185                 }catch(e){}
24186             };
24187         }
24188     },
24189
24190     // private
24191     handleResponse : function(o, trans){
24192         this.trans = false;
24193         this.destroyTrans(trans, true);
24194         var result;
24195         try {
24196             result = trans.reader.readRecords(o);
24197         }catch(e){
24198             this.fireEvent("loadexception", this, o, trans.arg, e);
24199             trans.callback.call(trans.scope||window, null, trans.arg, false);
24200             return;
24201         }
24202         this.fireEvent("load", this, o, trans.arg);
24203         trans.callback.call(trans.scope||window, result, trans.arg, true);
24204     },
24205
24206     // private
24207     handleFailure : function(trans){
24208         this.trans = false;
24209         this.destroyTrans(trans, false);
24210         this.fireEvent("loadexception", this, null, trans.arg);
24211         trans.callback.call(trans.scope||window, null, trans.arg, false);
24212     }
24213 });/*
24214  * Based on:
24215  * Ext JS Library 1.1.1
24216  * Copyright(c) 2006-2007, Ext JS, LLC.
24217  *
24218  * Originally Released Under LGPL - original licence link has changed is not relivant.
24219  *
24220  * Fork - LGPL
24221  * <script type="text/javascript">
24222  */
24223
24224 /**
24225  * @class Roo.data.JsonReader
24226  * @extends Roo.data.DataReader
24227  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24228  * based on mappings in a provided Roo.data.Record constructor.
24229  * 
24230  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24231  * in the reply previously. 
24232  * 
24233  * <p>
24234  * Example code:
24235  * <pre><code>
24236 var RecordDef = Roo.data.Record.create([
24237     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24238     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24239 ]);
24240 var myReader = new Roo.data.JsonReader({
24241     totalProperty: "results",    // The property which contains the total dataset size (optional)
24242     root: "rows",                // The property which contains an Array of row objects
24243     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24244 }, RecordDef);
24245 </code></pre>
24246  * <p>
24247  * This would consume a JSON file like this:
24248  * <pre><code>
24249 { 'results': 2, 'rows': [
24250     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24251     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24252 }
24253 </code></pre>
24254  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24255  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24256  * paged from the remote server.
24257  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24258  * @cfg {String} root name of the property which contains the Array of row objects.
24259  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24260  * @cfg {Array} fields Array of field definition objects
24261  * @constructor
24262  * Create a new JsonReader
24263  * @param {Object} meta Metadata configuration options
24264  * @param {Object} recordType Either an Array of field definition objects,
24265  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24266  */
24267 Roo.data.JsonReader = function(meta, recordType){
24268     
24269     meta = meta || {};
24270     // set some defaults:
24271     Roo.applyIf(meta, {
24272         totalProperty: 'total',
24273         successProperty : 'success',
24274         root : 'data',
24275         id : 'id'
24276     });
24277     
24278     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24279 };
24280 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24281     
24282     /**
24283      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24284      * Used by Store query builder to append _requestMeta to params.
24285      * 
24286      */
24287     metaFromRemote : false,
24288     /**
24289      * This method is only used by a DataProxy which has retrieved data from a remote server.
24290      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24291      * @return {Object} data A data block which is used by an Roo.data.Store object as
24292      * a cache of Roo.data.Records.
24293      */
24294     read : function(response){
24295         var json = response.responseText;
24296        
24297         var o = /* eval:var:o */ eval("("+json+")");
24298         if(!o) {
24299             throw {message: "JsonReader.read: Json object not found"};
24300         }
24301         
24302         if(o.metaData){
24303             
24304             delete this.ef;
24305             this.metaFromRemote = true;
24306             this.meta = o.metaData;
24307             this.recordType = Roo.data.Record.create(o.metaData.fields);
24308             this.onMetaChange(this.meta, this.recordType, o);
24309         }
24310         return this.readRecords(o);
24311     },
24312
24313     // private function a store will implement
24314     onMetaChange : function(meta, recordType, o){
24315
24316     },
24317
24318     /**
24319          * @ignore
24320          */
24321     simpleAccess: function(obj, subsc) {
24322         return obj[subsc];
24323     },
24324
24325         /**
24326          * @ignore
24327          */
24328     getJsonAccessor: function(){
24329         var re = /[\[\.]/;
24330         return function(expr) {
24331             try {
24332                 return(re.test(expr))
24333                     ? new Function("obj", "return obj." + expr)
24334                     : function(obj){
24335                         return obj[expr];
24336                     };
24337             } catch(e){}
24338             return Roo.emptyFn;
24339         };
24340     }(),
24341
24342     /**
24343      * Create a data block containing Roo.data.Records from an XML document.
24344      * @param {Object} o An object which contains an Array of row objects in the property specified
24345      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24346      * which contains the total size of the dataset.
24347      * @return {Object} data A data block which is used by an Roo.data.Store object as
24348      * a cache of Roo.data.Records.
24349      */
24350     readRecords : function(o){
24351         /**
24352          * After any data loads, the raw JSON data is available for further custom processing.
24353          * @type Object
24354          */
24355         this.o = o;
24356         var s = this.meta, Record = this.recordType,
24357             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24358
24359 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24360         if (!this.ef) {
24361             if(s.totalProperty) {
24362                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24363                 }
24364                 if(s.successProperty) {
24365                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24366                 }
24367                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24368                 if (s.id) {
24369                         var g = this.getJsonAccessor(s.id);
24370                         this.getId = function(rec) {
24371                                 var r = g(rec);  
24372                                 return (r === undefined || r === "") ? null : r;
24373                         };
24374                 } else {
24375                         this.getId = function(){return null;};
24376                 }
24377             this.ef = [];
24378             for(var jj = 0; jj < fl; jj++){
24379                 f = fi[jj];
24380                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24381                 this.ef[jj] = this.getJsonAccessor(map);
24382             }
24383         }
24384
24385         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24386         if(s.totalProperty){
24387             var vt = parseInt(this.getTotal(o), 10);
24388             if(!isNaN(vt)){
24389                 totalRecords = vt;
24390             }
24391         }
24392         if(s.successProperty){
24393             var vs = this.getSuccess(o);
24394             if(vs === false || vs === 'false'){
24395                 success = false;
24396             }
24397         }
24398         var records = [];
24399         for(var i = 0; i < c; i++){
24400                 var n = root[i];
24401             var values = {};
24402             var id = this.getId(n);
24403             for(var j = 0; j < fl; j++){
24404                 f = fi[j];
24405             var v = this.ef[j](n);
24406             if (!f.convert) {
24407                 Roo.log('missing convert for ' + f.name);
24408                 Roo.log(f);
24409                 continue;
24410             }
24411             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24412             }
24413             var record = new Record(values, id);
24414             record.json = n;
24415             records[i] = record;
24416         }
24417         return {
24418             raw : o,
24419             success : success,
24420             records : records,
24421             totalRecords : totalRecords
24422         };
24423     }
24424 });/*
24425  * Based on:
24426  * Ext JS Library 1.1.1
24427  * Copyright(c) 2006-2007, Ext JS, LLC.
24428  *
24429  * Originally Released Under LGPL - original licence link has changed is not relivant.
24430  *
24431  * Fork - LGPL
24432  * <script type="text/javascript">
24433  */
24434
24435 /**
24436  * @class Roo.data.XmlReader
24437  * @extends Roo.data.DataReader
24438  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24439  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24440  * <p>
24441  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24442  * header in the HTTP response must be set to "text/xml".</em>
24443  * <p>
24444  * Example code:
24445  * <pre><code>
24446 var RecordDef = Roo.data.Record.create([
24447    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24448    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24449 ]);
24450 var myReader = new Roo.data.XmlReader({
24451    totalRecords: "results", // The element which contains the total dataset size (optional)
24452    record: "row",           // The repeated element which contains row information
24453    id: "id"                 // The element within the row that provides an ID for the record (optional)
24454 }, RecordDef);
24455 </code></pre>
24456  * <p>
24457  * This would consume an XML file like this:
24458  * <pre><code>
24459 &lt;?xml?>
24460 &lt;dataset>
24461  &lt;results>2&lt;/results>
24462  &lt;row>
24463    &lt;id>1&lt;/id>
24464    &lt;name>Bill&lt;/name>
24465    &lt;occupation>Gardener&lt;/occupation>
24466  &lt;/row>
24467  &lt;row>
24468    &lt;id>2&lt;/id>
24469    &lt;name>Ben&lt;/name>
24470    &lt;occupation>Horticulturalist&lt;/occupation>
24471  &lt;/row>
24472 &lt;/dataset>
24473 </code></pre>
24474  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24475  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24476  * paged from the remote server.
24477  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24478  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24479  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24480  * a record identifier value.
24481  * @constructor
24482  * Create a new XmlReader
24483  * @param {Object} meta Metadata configuration options
24484  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24485  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24486  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24487  */
24488 Roo.data.XmlReader = function(meta, recordType){
24489     meta = meta || {};
24490     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24491 };
24492 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24493     /**
24494      * This method is only used by a DataProxy which has retrieved data from a remote server.
24495          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24496          * to contain a method called 'responseXML' that returns an XML document object.
24497      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24498      * a cache of Roo.data.Records.
24499      */
24500     read : function(response){
24501         var doc = response.responseXML;
24502         if(!doc) {
24503             throw {message: "XmlReader.read: XML Document not available"};
24504         }
24505         return this.readRecords(doc);
24506     },
24507
24508     /**
24509      * Create a data block containing Roo.data.Records from an XML document.
24510          * @param {Object} doc A parsed XML document.
24511      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24512      * a cache of Roo.data.Records.
24513      */
24514     readRecords : function(doc){
24515         /**
24516          * After any data loads/reads, the raw XML Document is available for further custom processing.
24517          * @type XMLDocument
24518          */
24519         this.xmlData = doc;
24520         var root = doc.documentElement || doc;
24521         var q = Roo.DomQuery;
24522         var recordType = this.recordType, fields = recordType.prototype.fields;
24523         var sid = this.meta.id;
24524         var totalRecords = 0, success = true;
24525         if(this.meta.totalRecords){
24526             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24527         }
24528         
24529         if(this.meta.success){
24530             var sv = q.selectValue(this.meta.success, root, true);
24531             success = sv !== false && sv !== 'false';
24532         }
24533         var records = [];
24534         var ns = q.select(this.meta.record, root);
24535         for(var i = 0, len = ns.length; i < len; i++) {
24536                 var n = ns[i];
24537                 var values = {};
24538                 var id = sid ? q.selectValue(sid, n) : undefined;
24539                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24540                     var f = fields.items[j];
24541                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24542                     v = f.convert(v);
24543                     values[f.name] = v;
24544                 }
24545                 var record = new recordType(values, id);
24546                 record.node = n;
24547                 records[records.length] = record;
24548             }
24549
24550             return {
24551                 success : success,
24552                 records : records,
24553                 totalRecords : totalRecords || records.length
24554             };
24555     }
24556 });/*
24557  * Based on:
24558  * Ext JS Library 1.1.1
24559  * Copyright(c) 2006-2007, Ext JS, LLC.
24560  *
24561  * Originally Released Under LGPL - original licence link has changed is not relivant.
24562  *
24563  * Fork - LGPL
24564  * <script type="text/javascript">
24565  */
24566
24567 /**
24568  * @class Roo.data.ArrayReader
24569  * @extends Roo.data.DataReader
24570  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24571  * Each element of that Array represents a row of data fields. The
24572  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24573  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24574  * <p>
24575  * Example code:.
24576  * <pre><code>
24577 var RecordDef = Roo.data.Record.create([
24578     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24579     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24580 ]);
24581 var myReader = new Roo.data.ArrayReader({
24582     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24583 }, RecordDef);
24584 </code></pre>
24585  * <p>
24586  * This would consume an Array like this:
24587  * <pre><code>
24588 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24589   </code></pre>
24590  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
24591  * @constructor
24592  * Create a new JsonReader
24593  * @param {Object} meta Metadata configuration options.
24594  * @param {Object} recordType Either an Array of field definition objects
24595  * as specified to {@link Roo.data.Record#create},
24596  * or an {@link Roo.data.Record} object
24597  * created using {@link Roo.data.Record#create}.
24598  */
24599 Roo.data.ArrayReader = function(meta, recordType){
24600     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
24601 };
24602
24603 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24604     /**
24605      * Create a data block containing Roo.data.Records from an XML document.
24606      * @param {Object} o An Array of row objects which represents the dataset.
24607      * @return {Object} data A data block which is used by an Roo.data.Store object as
24608      * a cache of Roo.data.Records.
24609      */
24610     readRecords : function(o){
24611         var sid = this.meta ? this.meta.id : null;
24612         var recordType = this.recordType, fields = recordType.prototype.fields;
24613         var records = [];
24614         var root = o;
24615             for(var i = 0; i < root.length; i++){
24616                     var n = root[i];
24617                 var values = {};
24618                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24619                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24620                 var f = fields.items[j];
24621                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24622                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24623                 v = f.convert(v);
24624                 values[f.name] = v;
24625             }
24626                 var record = new recordType(values, id);
24627                 record.json = n;
24628                 records[records.length] = record;
24629             }
24630             return {
24631                 records : records,
24632                 totalRecords : records.length
24633             };
24634     }
24635 });/*
24636  * Based on:
24637  * Ext JS Library 1.1.1
24638  * Copyright(c) 2006-2007, Ext JS, LLC.
24639  *
24640  * Originally Released Under LGPL - original licence link has changed is not relivant.
24641  *
24642  * Fork - LGPL
24643  * <script type="text/javascript">
24644  */
24645
24646
24647 /**
24648  * @class Roo.data.Tree
24649  * @extends Roo.util.Observable
24650  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24651  * in the tree have most standard DOM functionality.
24652  * @constructor
24653  * @param {Node} root (optional) The root node
24654  */
24655 Roo.data.Tree = function(root){
24656    this.nodeHash = {};
24657    /**
24658     * The root node for this tree
24659     * @type Node
24660     */
24661    this.root = null;
24662    if(root){
24663        this.setRootNode(root);
24664    }
24665    this.addEvents({
24666        /**
24667         * @event append
24668         * Fires when a new child node is appended to a node in this tree.
24669         * @param {Tree} tree The owner tree
24670         * @param {Node} parent The parent node
24671         * @param {Node} node The newly appended node
24672         * @param {Number} index The index of the newly appended node
24673         */
24674        "append" : true,
24675        /**
24676         * @event remove
24677         * Fires when a child node is removed from a node in this tree.
24678         * @param {Tree} tree The owner tree
24679         * @param {Node} parent The parent node
24680         * @param {Node} node The child node removed
24681         */
24682        "remove" : true,
24683        /**
24684         * @event move
24685         * Fires when a node is moved to a new location in the tree
24686         * @param {Tree} tree The owner tree
24687         * @param {Node} node The node moved
24688         * @param {Node} oldParent The old parent of this node
24689         * @param {Node} newParent The new parent of this node
24690         * @param {Number} index The index it was moved to
24691         */
24692        "move" : true,
24693        /**
24694         * @event insert
24695         * Fires when a new child node is inserted in a node in this tree.
24696         * @param {Tree} tree The owner tree
24697         * @param {Node} parent The parent node
24698         * @param {Node} node The child node inserted
24699         * @param {Node} refNode The child node the node was inserted before
24700         */
24701        "insert" : true,
24702        /**
24703         * @event beforeappend
24704         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24705         * @param {Tree} tree The owner tree
24706         * @param {Node} parent The parent node
24707         * @param {Node} node The child node to be appended
24708         */
24709        "beforeappend" : true,
24710        /**
24711         * @event beforeremove
24712         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24713         * @param {Tree} tree The owner tree
24714         * @param {Node} parent The parent node
24715         * @param {Node} node The child node to be removed
24716         */
24717        "beforeremove" : true,
24718        /**
24719         * @event beforemove
24720         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24721         * @param {Tree} tree The owner tree
24722         * @param {Node} node The node being moved
24723         * @param {Node} oldParent The parent of the node
24724         * @param {Node} newParent The new parent the node is moving to
24725         * @param {Number} index The index it is being moved to
24726         */
24727        "beforemove" : true,
24728        /**
24729         * @event beforeinsert
24730         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24731         * @param {Tree} tree The owner tree
24732         * @param {Node} parent The parent node
24733         * @param {Node} node The child node to be inserted
24734         * @param {Node} refNode The child node the node is being inserted before
24735         */
24736        "beforeinsert" : true
24737    });
24738
24739     Roo.data.Tree.superclass.constructor.call(this);
24740 };
24741
24742 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24743     pathSeparator: "/",
24744
24745     proxyNodeEvent : function(){
24746         return this.fireEvent.apply(this, arguments);
24747     },
24748
24749     /**
24750      * Returns the root node for this tree.
24751      * @return {Node}
24752      */
24753     getRootNode : function(){
24754         return this.root;
24755     },
24756
24757     /**
24758      * Sets the root node for this tree.
24759      * @param {Node} node
24760      * @return {Node}
24761      */
24762     setRootNode : function(node){
24763         this.root = node;
24764         node.ownerTree = this;
24765         node.isRoot = true;
24766         this.registerNode(node);
24767         return node;
24768     },
24769
24770     /**
24771      * Gets a node in this tree by its id.
24772      * @param {String} id
24773      * @return {Node}
24774      */
24775     getNodeById : function(id){
24776         return this.nodeHash[id];
24777     },
24778
24779     registerNode : function(node){
24780         this.nodeHash[node.id] = node;
24781     },
24782
24783     unregisterNode : function(node){
24784         delete this.nodeHash[node.id];
24785     },
24786
24787     toString : function(){
24788         return "[Tree"+(this.id?" "+this.id:"")+"]";
24789     }
24790 });
24791
24792 /**
24793  * @class Roo.data.Node
24794  * @extends Roo.util.Observable
24795  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24796  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24797  * @constructor
24798  * @param {Object} attributes The attributes/config for the node
24799  */
24800 Roo.data.Node = function(attributes){
24801     /**
24802      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24803      * @type {Object}
24804      */
24805     this.attributes = attributes || {};
24806     this.leaf = this.attributes.leaf;
24807     /**
24808      * The node id. @type String
24809      */
24810     this.id = this.attributes.id;
24811     if(!this.id){
24812         this.id = Roo.id(null, "ynode-");
24813         this.attributes.id = this.id;
24814     }
24815      
24816     
24817     /**
24818      * All child nodes of this node. @type Array
24819      */
24820     this.childNodes = [];
24821     if(!this.childNodes.indexOf){ // indexOf is a must
24822         this.childNodes.indexOf = function(o){
24823             for(var i = 0, len = this.length; i < len; i++){
24824                 if(this[i] == o) {
24825                     return i;
24826                 }
24827             }
24828             return -1;
24829         };
24830     }
24831     /**
24832      * The parent node for this node. @type Node
24833      */
24834     this.parentNode = null;
24835     /**
24836      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24837      */
24838     this.firstChild = null;
24839     /**
24840      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24841      */
24842     this.lastChild = null;
24843     /**
24844      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24845      */
24846     this.previousSibling = null;
24847     /**
24848      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24849      */
24850     this.nextSibling = null;
24851
24852     this.addEvents({
24853        /**
24854         * @event append
24855         * Fires when a new child node is appended
24856         * @param {Tree} tree The owner tree
24857         * @param {Node} this This node
24858         * @param {Node} node The newly appended node
24859         * @param {Number} index The index of the newly appended node
24860         */
24861        "append" : true,
24862        /**
24863         * @event remove
24864         * Fires when a child node is removed
24865         * @param {Tree} tree The owner tree
24866         * @param {Node} this This node
24867         * @param {Node} node The removed node
24868         */
24869        "remove" : true,
24870        /**
24871         * @event move
24872         * Fires when this node is moved to a new location in the tree
24873         * @param {Tree} tree The owner tree
24874         * @param {Node} this This node
24875         * @param {Node} oldParent The old parent of this node
24876         * @param {Node} newParent The new parent of this node
24877         * @param {Number} index The index it was moved to
24878         */
24879        "move" : true,
24880        /**
24881         * @event insert
24882         * Fires when a new child node is inserted.
24883         * @param {Tree} tree The owner tree
24884         * @param {Node} this This node
24885         * @param {Node} node The child node inserted
24886         * @param {Node} refNode The child node the node was inserted before
24887         */
24888        "insert" : true,
24889        /**
24890         * @event beforeappend
24891         * Fires before a new child is appended, return false to cancel the append.
24892         * @param {Tree} tree The owner tree
24893         * @param {Node} this This node
24894         * @param {Node} node The child node to be appended
24895         */
24896        "beforeappend" : true,
24897        /**
24898         * @event beforeremove
24899         * Fires before a child is removed, return false to cancel the remove.
24900         * @param {Tree} tree The owner tree
24901         * @param {Node} this This node
24902         * @param {Node} node The child node to be removed
24903         */
24904        "beforeremove" : true,
24905        /**
24906         * @event beforemove
24907         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24908         * @param {Tree} tree The owner tree
24909         * @param {Node} this This node
24910         * @param {Node} oldParent The parent of this node
24911         * @param {Node} newParent The new parent this node is moving to
24912         * @param {Number} index The index it is being moved to
24913         */
24914        "beforemove" : true,
24915        /**
24916         * @event beforeinsert
24917         * Fires before a new child is inserted, return false to cancel the insert.
24918         * @param {Tree} tree The owner tree
24919         * @param {Node} this This node
24920         * @param {Node} node The child node to be inserted
24921         * @param {Node} refNode The child node the node is being inserted before
24922         */
24923        "beforeinsert" : true
24924    });
24925     this.listeners = this.attributes.listeners;
24926     Roo.data.Node.superclass.constructor.call(this);
24927 };
24928
24929 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24930     fireEvent : function(evtName){
24931         // first do standard event for this node
24932         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24933             return false;
24934         }
24935         // then bubble it up to the tree if the event wasn't cancelled
24936         var ot = this.getOwnerTree();
24937         if(ot){
24938             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24939                 return false;
24940             }
24941         }
24942         return true;
24943     },
24944
24945     /**
24946      * Returns true if this node is a leaf
24947      * @return {Boolean}
24948      */
24949     isLeaf : function(){
24950         return this.leaf === true;
24951     },
24952
24953     // private
24954     setFirstChild : function(node){
24955         this.firstChild = node;
24956     },
24957
24958     //private
24959     setLastChild : function(node){
24960         this.lastChild = node;
24961     },
24962
24963
24964     /**
24965      * Returns true if this node is the last child of its parent
24966      * @return {Boolean}
24967      */
24968     isLast : function(){
24969        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24970     },
24971
24972     /**
24973      * Returns true if this node is the first child of its parent
24974      * @return {Boolean}
24975      */
24976     isFirst : function(){
24977        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24978     },
24979
24980     hasChildNodes : function(){
24981         return !this.isLeaf() && this.childNodes.length > 0;
24982     },
24983
24984     /**
24985      * Insert node(s) as the last child node of this node.
24986      * @param {Node/Array} node The node or Array of nodes to append
24987      * @return {Node} The appended node if single append, or null if an array was passed
24988      */
24989     appendChild : function(node){
24990         var multi = false;
24991         if(node instanceof Array){
24992             multi = node;
24993         }else if(arguments.length > 1){
24994             multi = arguments;
24995         }
24996         // if passed an array or multiple args do them one by one
24997         if(multi){
24998             for(var i = 0, len = multi.length; i < len; i++) {
24999                 this.appendChild(multi[i]);
25000             }
25001         }else{
25002             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25003                 return false;
25004             }
25005             var index = this.childNodes.length;
25006             var oldParent = node.parentNode;
25007             // it's a move, make sure we move it cleanly
25008             if(oldParent){
25009                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25010                     return false;
25011                 }
25012                 oldParent.removeChild(node);
25013             }
25014             index = this.childNodes.length;
25015             if(index == 0){
25016                 this.setFirstChild(node);
25017             }
25018             this.childNodes.push(node);
25019             node.parentNode = this;
25020             var ps = this.childNodes[index-1];
25021             if(ps){
25022                 node.previousSibling = ps;
25023                 ps.nextSibling = node;
25024             }else{
25025                 node.previousSibling = null;
25026             }
25027             node.nextSibling = null;
25028             this.setLastChild(node);
25029             node.setOwnerTree(this.getOwnerTree());
25030             this.fireEvent("append", this.ownerTree, this, node, index);
25031             if(oldParent){
25032                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25033             }
25034             return node;
25035         }
25036     },
25037
25038     /**
25039      * Removes a child node from this node.
25040      * @param {Node} node The node to remove
25041      * @return {Node} The removed node
25042      */
25043     removeChild : function(node){
25044         var index = this.childNodes.indexOf(node);
25045         if(index == -1){
25046             return false;
25047         }
25048         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25049             return false;
25050         }
25051
25052         // remove it from childNodes collection
25053         this.childNodes.splice(index, 1);
25054
25055         // update siblings
25056         if(node.previousSibling){
25057             node.previousSibling.nextSibling = node.nextSibling;
25058         }
25059         if(node.nextSibling){
25060             node.nextSibling.previousSibling = node.previousSibling;
25061         }
25062
25063         // update child refs
25064         if(this.firstChild == node){
25065             this.setFirstChild(node.nextSibling);
25066         }
25067         if(this.lastChild == node){
25068             this.setLastChild(node.previousSibling);
25069         }
25070
25071         node.setOwnerTree(null);
25072         // clear any references from the node
25073         node.parentNode = null;
25074         node.previousSibling = null;
25075         node.nextSibling = null;
25076         this.fireEvent("remove", this.ownerTree, this, node);
25077         return node;
25078     },
25079
25080     /**
25081      * Inserts the first node before the second node in this nodes childNodes collection.
25082      * @param {Node} node The node to insert
25083      * @param {Node} refNode The node to insert before (if null the node is appended)
25084      * @return {Node} The inserted node
25085      */
25086     insertBefore : function(node, refNode){
25087         if(!refNode){ // like standard Dom, refNode can be null for append
25088             return this.appendChild(node);
25089         }
25090         // nothing to do
25091         if(node == refNode){
25092             return false;
25093         }
25094
25095         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25096             return false;
25097         }
25098         var index = this.childNodes.indexOf(refNode);
25099         var oldParent = node.parentNode;
25100         var refIndex = index;
25101
25102         // when moving internally, indexes will change after remove
25103         if(oldParent == this && this.childNodes.indexOf(node) < index){
25104             refIndex--;
25105         }
25106
25107         // it's a move, make sure we move it cleanly
25108         if(oldParent){
25109             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25110                 return false;
25111             }
25112             oldParent.removeChild(node);
25113         }
25114         if(refIndex == 0){
25115             this.setFirstChild(node);
25116         }
25117         this.childNodes.splice(refIndex, 0, node);
25118         node.parentNode = this;
25119         var ps = this.childNodes[refIndex-1];
25120         if(ps){
25121             node.previousSibling = ps;
25122             ps.nextSibling = node;
25123         }else{
25124             node.previousSibling = null;
25125         }
25126         node.nextSibling = refNode;
25127         refNode.previousSibling = node;
25128         node.setOwnerTree(this.getOwnerTree());
25129         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25130         if(oldParent){
25131             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25132         }
25133         return node;
25134     },
25135
25136     /**
25137      * Returns the child node at the specified index.
25138      * @param {Number} index
25139      * @return {Node}
25140      */
25141     item : function(index){
25142         return this.childNodes[index];
25143     },
25144
25145     /**
25146      * Replaces one child node in this node with another.
25147      * @param {Node} newChild The replacement node
25148      * @param {Node} oldChild The node to replace
25149      * @return {Node} The replaced node
25150      */
25151     replaceChild : function(newChild, oldChild){
25152         this.insertBefore(newChild, oldChild);
25153         this.removeChild(oldChild);
25154         return oldChild;
25155     },
25156
25157     /**
25158      * Returns the index of a child node
25159      * @param {Node} node
25160      * @return {Number} The index of the node or -1 if it was not found
25161      */
25162     indexOf : function(child){
25163         return this.childNodes.indexOf(child);
25164     },
25165
25166     /**
25167      * Returns the tree this node is in.
25168      * @return {Tree}
25169      */
25170     getOwnerTree : function(){
25171         // if it doesn't have one, look for one
25172         if(!this.ownerTree){
25173             var p = this;
25174             while(p){
25175                 if(p.ownerTree){
25176                     this.ownerTree = p.ownerTree;
25177                     break;
25178                 }
25179                 p = p.parentNode;
25180             }
25181         }
25182         return this.ownerTree;
25183     },
25184
25185     /**
25186      * Returns depth of this node (the root node has a depth of 0)
25187      * @return {Number}
25188      */
25189     getDepth : function(){
25190         var depth = 0;
25191         var p = this;
25192         while(p.parentNode){
25193             ++depth;
25194             p = p.parentNode;
25195         }
25196         return depth;
25197     },
25198
25199     // private
25200     setOwnerTree : function(tree){
25201         // if it's move, we need to update everyone
25202         if(tree != this.ownerTree){
25203             if(this.ownerTree){
25204                 this.ownerTree.unregisterNode(this);
25205             }
25206             this.ownerTree = tree;
25207             var cs = this.childNodes;
25208             for(var i = 0, len = cs.length; i < len; i++) {
25209                 cs[i].setOwnerTree(tree);
25210             }
25211             if(tree){
25212                 tree.registerNode(this);
25213             }
25214         }
25215     },
25216
25217     /**
25218      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25219      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25220      * @return {String} The path
25221      */
25222     getPath : function(attr){
25223         attr = attr || "id";
25224         var p = this.parentNode;
25225         var b = [this.attributes[attr]];
25226         while(p){
25227             b.unshift(p.attributes[attr]);
25228             p = p.parentNode;
25229         }
25230         var sep = this.getOwnerTree().pathSeparator;
25231         return sep + b.join(sep);
25232     },
25233
25234     /**
25235      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25236      * function call will be the scope provided or the current node. The arguments to the function
25237      * will be the args provided or the current node. If the function returns false at any point,
25238      * the bubble is stopped.
25239      * @param {Function} fn The function to call
25240      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25241      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25242      */
25243     bubble : function(fn, scope, args){
25244         var p = this;
25245         while(p){
25246             if(fn.call(scope || p, args || p) === false){
25247                 break;
25248             }
25249             p = p.parentNode;
25250         }
25251     },
25252
25253     /**
25254      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25255      * function call will be the scope provided or the current node. The arguments to the function
25256      * will be the args provided or the current node. If the function returns false at any point,
25257      * the cascade is stopped on that branch.
25258      * @param {Function} fn The function to call
25259      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25260      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25261      */
25262     cascade : function(fn, scope, args){
25263         if(fn.call(scope || this, args || this) !== false){
25264             var cs = this.childNodes;
25265             for(var i = 0, len = cs.length; i < len; i++) {
25266                 cs[i].cascade(fn, scope, args);
25267             }
25268         }
25269     },
25270
25271     /**
25272      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25273      * function call will be the scope provided or the current node. The arguments to the function
25274      * will be the args provided or the current node. If the function returns false at any point,
25275      * the iteration stops.
25276      * @param {Function} fn The function to call
25277      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25278      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25279      */
25280     eachChild : function(fn, scope, args){
25281         var cs = this.childNodes;
25282         for(var i = 0, len = cs.length; i < len; i++) {
25283                 if(fn.call(scope || this, args || cs[i]) === false){
25284                     break;
25285                 }
25286         }
25287     },
25288
25289     /**
25290      * Finds the first child that has the attribute with the specified value.
25291      * @param {String} attribute The attribute name
25292      * @param {Mixed} value The value to search for
25293      * @return {Node} The found child or null if none was found
25294      */
25295     findChild : function(attribute, value){
25296         var cs = this.childNodes;
25297         for(var i = 0, len = cs.length; i < len; i++) {
25298                 if(cs[i].attributes[attribute] == value){
25299                     return cs[i];
25300                 }
25301         }
25302         return null;
25303     },
25304
25305     /**
25306      * Finds the first child by a custom function. The child matches if the function passed
25307      * returns true.
25308      * @param {Function} fn
25309      * @param {Object} scope (optional)
25310      * @return {Node} The found child or null if none was found
25311      */
25312     findChildBy : function(fn, scope){
25313         var cs = this.childNodes;
25314         for(var i = 0, len = cs.length; i < len; i++) {
25315                 if(fn.call(scope||cs[i], cs[i]) === true){
25316                     return cs[i];
25317                 }
25318         }
25319         return null;
25320     },
25321
25322     /**
25323      * Sorts this nodes children using the supplied sort function
25324      * @param {Function} fn
25325      * @param {Object} scope (optional)
25326      */
25327     sort : function(fn, scope){
25328         var cs = this.childNodes;
25329         var len = cs.length;
25330         if(len > 0){
25331             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25332             cs.sort(sortFn);
25333             for(var i = 0; i < len; i++){
25334                 var n = cs[i];
25335                 n.previousSibling = cs[i-1];
25336                 n.nextSibling = cs[i+1];
25337                 if(i == 0){
25338                     this.setFirstChild(n);
25339                 }
25340                 if(i == len-1){
25341                     this.setLastChild(n);
25342                 }
25343             }
25344         }
25345     },
25346
25347     /**
25348      * Returns true if this node is an ancestor (at any point) of the passed node.
25349      * @param {Node} node
25350      * @return {Boolean}
25351      */
25352     contains : function(node){
25353         return node.isAncestor(this);
25354     },
25355
25356     /**
25357      * Returns true if the passed node is an ancestor (at any point) of this node.
25358      * @param {Node} node
25359      * @return {Boolean}
25360      */
25361     isAncestor : function(node){
25362         var p = this.parentNode;
25363         while(p){
25364             if(p == node){
25365                 return true;
25366             }
25367             p = p.parentNode;
25368         }
25369         return false;
25370     },
25371
25372     toString : function(){
25373         return "[Node"+(this.id?" "+this.id:"")+"]";
25374     }
25375 });/*
25376  * Based on:
25377  * Ext JS Library 1.1.1
25378  * Copyright(c) 2006-2007, Ext JS, LLC.
25379  *
25380  * Originally Released Under LGPL - original licence link has changed is not relivant.
25381  *
25382  * Fork - LGPL
25383  * <script type="text/javascript">
25384  */
25385  (function(){ 
25386 /**
25387  * @class Roo.Layer
25388  * @extends Roo.Element
25389  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25390  * automatic maintaining of shadow/shim positions.
25391  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25392  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25393  * you can pass a string with a CSS class name. False turns off the shadow.
25394  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25395  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25396  * @cfg {String} cls CSS class to add to the element
25397  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25398  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25399  * @constructor
25400  * @param {Object} config An object with config options.
25401  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25402  */
25403
25404 Roo.Layer = function(config, existingEl){
25405     config = config || {};
25406     var dh = Roo.DomHelper;
25407     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25408     if(existingEl){
25409         this.dom = Roo.getDom(existingEl);
25410     }
25411     if(!this.dom){
25412         var o = config.dh || {tag: "div", cls: "x-layer"};
25413         this.dom = dh.append(pel, o);
25414     }
25415     if(config.cls){
25416         this.addClass(config.cls);
25417     }
25418     this.constrain = config.constrain !== false;
25419     this.visibilityMode = Roo.Element.VISIBILITY;
25420     if(config.id){
25421         this.id = this.dom.id = config.id;
25422     }else{
25423         this.id = Roo.id(this.dom);
25424     }
25425     this.zindex = config.zindex || this.getZIndex();
25426     this.position("absolute", this.zindex);
25427     if(config.shadow){
25428         this.shadowOffset = config.shadowOffset || 4;
25429         this.shadow = new Roo.Shadow({
25430             offset : this.shadowOffset,
25431             mode : config.shadow
25432         });
25433     }else{
25434         this.shadowOffset = 0;
25435     }
25436     this.useShim = config.shim !== false && Roo.useShims;
25437     this.useDisplay = config.useDisplay;
25438     this.hide();
25439 };
25440
25441 var supr = Roo.Element.prototype;
25442
25443 // shims are shared among layer to keep from having 100 iframes
25444 var shims = [];
25445
25446 Roo.extend(Roo.Layer, Roo.Element, {
25447
25448     getZIndex : function(){
25449         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25450     },
25451
25452     getShim : function(){
25453         if(!this.useShim){
25454             return null;
25455         }
25456         if(this.shim){
25457             return this.shim;
25458         }
25459         var shim = shims.shift();
25460         if(!shim){
25461             shim = this.createShim();
25462             shim.enableDisplayMode('block');
25463             shim.dom.style.display = 'none';
25464             shim.dom.style.visibility = 'visible';
25465         }
25466         var pn = this.dom.parentNode;
25467         if(shim.dom.parentNode != pn){
25468             pn.insertBefore(shim.dom, this.dom);
25469         }
25470         shim.setStyle('z-index', this.getZIndex()-2);
25471         this.shim = shim;
25472         return shim;
25473     },
25474
25475     hideShim : function(){
25476         if(this.shim){
25477             this.shim.setDisplayed(false);
25478             shims.push(this.shim);
25479             delete this.shim;
25480         }
25481     },
25482
25483     disableShadow : function(){
25484         if(this.shadow){
25485             this.shadowDisabled = true;
25486             this.shadow.hide();
25487             this.lastShadowOffset = this.shadowOffset;
25488             this.shadowOffset = 0;
25489         }
25490     },
25491
25492     enableShadow : function(show){
25493         if(this.shadow){
25494             this.shadowDisabled = false;
25495             this.shadowOffset = this.lastShadowOffset;
25496             delete this.lastShadowOffset;
25497             if(show){
25498                 this.sync(true);
25499             }
25500         }
25501     },
25502
25503     // private
25504     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25505     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25506     sync : function(doShow){
25507         var sw = this.shadow;
25508         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25509             var sh = this.getShim();
25510
25511             var w = this.getWidth(),
25512                 h = this.getHeight();
25513
25514             var l = this.getLeft(true),
25515                 t = this.getTop(true);
25516
25517             if(sw && !this.shadowDisabled){
25518                 if(doShow && !sw.isVisible()){
25519                     sw.show(this);
25520                 }else{
25521                     sw.realign(l, t, w, h);
25522                 }
25523                 if(sh){
25524                     if(doShow){
25525                        sh.show();
25526                     }
25527                     // fit the shim behind the shadow, so it is shimmed too
25528                     var a = sw.adjusts, s = sh.dom.style;
25529                     s.left = (Math.min(l, l+a.l))+"px";
25530                     s.top = (Math.min(t, t+a.t))+"px";
25531                     s.width = (w+a.w)+"px";
25532                     s.height = (h+a.h)+"px";
25533                 }
25534             }else if(sh){
25535                 if(doShow){
25536                    sh.show();
25537                 }
25538                 sh.setSize(w, h);
25539                 sh.setLeftTop(l, t);
25540             }
25541             
25542         }
25543     },
25544
25545     // private
25546     destroy : function(){
25547         this.hideShim();
25548         if(this.shadow){
25549             this.shadow.hide();
25550         }
25551         this.removeAllListeners();
25552         var pn = this.dom.parentNode;
25553         if(pn){
25554             pn.removeChild(this.dom);
25555         }
25556         Roo.Element.uncache(this.id);
25557     },
25558
25559     remove : function(){
25560         this.destroy();
25561     },
25562
25563     // private
25564     beginUpdate : function(){
25565         this.updating = true;
25566     },
25567
25568     // private
25569     endUpdate : function(){
25570         this.updating = false;
25571         this.sync(true);
25572     },
25573
25574     // private
25575     hideUnders : function(negOffset){
25576         if(this.shadow){
25577             this.shadow.hide();
25578         }
25579         this.hideShim();
25580     },
25581
25582     // private
25583     constrainXY : function(){
25584         if(this.constrain){
25585             var vw = Roo.lib.Dom.getViewWidth(),
25586                 vh = Roo.lib.Dom.getViewHeight();
25587             var s = Roo.get(document).getScroll();
25588
25589             var xy = this.getXY();
25590             var x = xy[0], y = xy[1];   
25591             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25592             // only move it if it needs it
25593             var moved = false;
25594             // first validate right/bottom
25595             if((x + w) > vw+s.left){
25596                 x = vw - w - this.shadowOffset;
25597                 moved = true;
25598             }
25599             if((y + h) > vh+s.top){
25600                 y = vh - h - this.shadowOffset;
25601                 moved = true;
25602             }
25603             // then make sure top/left isn't negative
25604             if(x < s.left){
25605                 x = s.left;
25606                 moved = true;
25607             }
25608             if(y < s.top){
25609                 y = s.top;
25610                 moved = true;
25611             }
25612             if(moved){
25613                 if(this.avoidY){
25614                     var ay = this.avoidY;
25615                     if(y <= ay && (y+h) >= ay){
25616                         y = ay-h-5;   
25617                     }
25618                 }
25619                 xy = [x, y];
25620                 this.storeXY(xy);
25621                 supr.setXY.call(this, xy);
25622                 this.sync();
25623             }
25624         }
25625     },
25626
25627     isVisible : function(){
25628         return this.visible;    
25629     },
25630
25631     // private
25632     showAction : function(){
25633         this.visible = true; // track visibility to prevent getStyle calls
25634         if(this.useDisplay === true){
25635             this.setDisplayed("");
25636         }else if(this.lastXY){
25637             supr.setXY.call(this, this.lastXY);
25638         }else if(this.lastLT){
25639             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25640         }
25641     },
25642
25643     // private
25644     hideAction : function(){
25645         this.visible = false;
25646         if(this.useDisplay === true){
25647             this.setDisplayed(false);
25648         }else{
25649             this.setLeftTop(-10000,-10000);
25650         }
25651     },
25652
25653     // overridden Element method
25654     setVisible : function(v, a, d, c, e){
25655         if(v){
25656             this.showAction();
25657         }
25658         if(a && v){
25659             var cb = function(){
25660                 this.sync(true);
25661                 if(c){
25662                     c();
25663                 }
25664             }.createDelegate(this);
25665             supr.setVisible.call(this, true, true, d, cb, e);
25666         }else{
25667             if(!v){
25668                 this.hideUnders(true);
25669             }
25670             var cb = c;
25671             if(a){
25672                 cb = function(){
25673                     this.hideAction();
25674                     if(c){
25675                         c();
25676                     }
25677                 }.createDelegate(this);
25678             }
25679             supr.setVisible.call(this, v, a, d, cb, e);
25680             if(v){
25681                 this.sync(true);
25682             }else if(!a){
25683                 this.hideAction();
25684             }
25685         }
25686     },
25687
25688     storeXY : function(xy){
25689         delete this.lastLT;
25690         this.lastXY = xy;
25691     },
25692
25693     storeLeftTop : function(left, top){
25694         delete this.lastXY;
25695         this.lastLT = [left, top];
25696     },
25697
25698     // private
25699     beforeFx : function(){
25700         this.beforeAction();
25701         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25702     },
25703
25704     // private
25705     afterFx : function(){
25706         Roo.Layer.superclass.afterFx.apply(this, arguments);
25707         this.sync(this.isVisible());
25708     },
25709
25710     // private
25711     beforeAction : function(){
25712         if(!this.updating && this.shadow){
25713             this.shadow.hide();
25714         }
25715     },
25716
25717     // overridden Element method
25718     setLeft : function(left){
25719         this.storeLeftTop(left, this.getTop(true));
25720         supr.setLeft.apply(this, arguments);
25721         this.sync();
25722     },
25723
25724     setTop : function(top){
25725         this.storeLeftTop(this.getLeft(true), top);
25726         supr.setTop.apply(this, arguments);
25727         this.sync();
25728     },
25729
25730     setLeftTop : function(left, top){
25731         this.storeLeftTop(left, top);
25732         supr.setLeftTop.apply(this, arguments);
25733         this.sync();
25734     },
25735
25736     setXY : function(xy, a, d, c, e){
25737         this.fixDisplay();
25738         this.beforeAction();
25739         this.storeXY(xy);
25740         var cb = this.createCB(c);
25741         supr.setXY.call(this, xy, a, d, cb, e);
25742         if(!a){
25743             cb();
25744         }
25745     },
25746
25747     // private
25748     createCB : function(c){
25749         var el = this;
25750         return function(){
25751             el.constrainXY();
25752             el.sync(true);
25753             if(c){
25754                 c();
25755             }
25756         };
25757     },
25758
25759     // overridden Element method
25760     setX : function(x, a, d, c, e){
25761         this.setXY([x, this.getY()], a, d, c, e);
25762     },
25763
25764     // overridden Element method
25765     setY : function(y, a, d, c, e){
25766         this.setXY([this.getX(), y], a, d, c, e);
25767     },
25768
25769     // overridden Element method
25770     setSize : function(w, h, a, d, c, e){
25771         this.beforeAction();
25772         var cb = this.createCB(c);
25773         supr.setSize.call(this, w, h, a, d, cb, e);
25774         if(!a){
25775             cb();
25776         }
25777     },
25778
25779     // overridden Element method
25780     setWidth : function(w, a, d, c, e){
25781         this.beforeAction();
25782         var cb = this.createCB(c);
25783         supr.setWidth.call(this, w, a, d, cb, e);
25784         if(!a){
25785             cb();
25786         }
25787     },
25788
25789     // overridden Element method
25790     setHeight : function(h, a, d, c, e){
25791         this.beforeAction();
25792         var cb = this.createCB(c);
25793         supr.setHeight.call(this, h, a, d, cb, e);
25794         if(!a){
25795             cb();
25796         }
25797     },
25798
25799     // overridden Element method
25800     setBounds : function(x, y, w, h, a, d, c, e){
25801         this.beforeAction();
25802         var cb = this.createCB(c);
25803         if(!a){
25804             this.storeXY([x, y]);
25805             supr.setXY.call(this, [x, y]);
25806             supr.setSize.call(this, w, h, a, d, cb, e);
25807             cb();
25808         }else{
25809             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25810         }
25811         return this;
25812     },
25813     
25814     /**
25815      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25816      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25817      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25818      * @param {Number} zindex The new z-index to set
25819      * @return {this} The Layer
25820      */
25821     setZIndex : function(zindex){
25822         this.zindex = zindex;
25823         this.setStyle("z-index", zindex + 2);
25824         if(this.shadow){
25825             this.shadow.setZIndex(zindex + 1);
25826         }
25827         if(this.shim){
25828             this.shim.setStyle("z-index", zindex);
25829         }
25830     }
25831 });
25832 })();/*
25833  * Based on:
25834  * Ext JS Library 1.1.1
25835  * Copyright(c) 2006-2007, Ext JS, LLC.
25836  *
25837  * Originally Released Under LGPL - original licence link has changed is not relivant.
25838  *
25839  * Fork - LGPL
25840  * <script type="text/javascript">
25841  */
25842
25843
25844 /**
25845  * @class Roo.Shadow
25846  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25847  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25848  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25849  * @constructor
25850  * Create a new Shadow
25851  * @param {Object} config The config object
25852  */
25853 Roo.Shadow = function(config){
25854     Roo.apply(this, config);
25855     if(typeof this.mode != "string"){
25856         this.mode = this.defaultMode;
25857     }
25858     var o = this.offset, a = {h: 0};
25859     var rad = Math.floor(this.offset/2);
25860     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25861         case "drop":
25862             a.w = 0;
25863             a.l = a.t = o;
25864             a.t -= 1;
25865             if(Roo.isIE){
25866                 a.l -= this.offset + rad;
25867                 a.t -= this.offset + rad;
25868                 a.w -= rad;
25869                 a.h -= rad;
25870                 a.t += 1;
25871             }
25872         break;
25873         case "sides":
25874             a.w = (o*2);
25875             a.l = -o;
25876             a.t = o-1;
25877             if(Roo.isIE){
25878                 a.l -= (this.offset - rad);
25879                 a.t -= this.offset + rad;
25880                 a.l += 1;
25881                 a.w -= (this.offset - rad)*2;
25882                 a.w -= rad + 1;
25883                 a.h -= 1;
25884             }
25885         break;
25886         case "frame":
25887             a.w = a.h = (o*2);
25888             a.l = a.t = -o;
25889             a.t += 1;
25890             a.h -= 2;
25891             if(Roo.isIE){
25892                 a.l -= (this.offset - rad);
25893                 a.t -= (this.offset - rad);
25894                 a.l += 1;
25895                 a.w -= (this.offset + rad + 1);
25896                 a.h -= (this.offset + rad);
25897                 a.h += 1;
25898             }
25899         break;
25900     };
25901
25902     this.adjusts = a;
25903 };
25904
25905 Roo.Shadow.prototype = {
25906     /**
25907      * @cfg {String} mode
25908      * The shadow display mode.  Supports the following options:<br />
25909      * sides: Shadow displays on both sides and bottom only<br />
25910      * frame: Shadow displays equally on all four sides<br />
25911      * drop: Traditional bottom-right drop shadow (default)
25912      */
25913     /**
25914      * @cfg {String} offset
25915      * The number of pixels to offset the shadow from the element (defaults to 4)
25916      */
25917     offset: 4,
25918
25919     // private
25920     defaultMode: "drop",
25921
25922     /**
25923      * Displays the shadow under the target element
25924      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25925      */
25926     show : function(target){
25927         target = Roo.get(target);
25928         if(!this.el){
25929             this.el = Roo.Shadow.Pool.pull();
25930             if(this.el.dom.nextSibling != target.dom){
25931                 this.el.insertBefore(target);
25932             }
25933         }
25934         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25935         if(Roo.isIE){
25936             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25937         }
25938         this.realign(
25939             target.getLeft(true),
25940             target.getTop(true),
25941             target.getWidth(),
25942             target.getHeight()
25943         );
25944         this.el.dom.style.display = "block";
25945     },
25946
25947     /**
25948      * Returns true if the shadow is visible, else false
25949      */
25950     isVisible : function(){
25951         return this.el ? true : false;  
25952     },
25953
25954     /**
25955      * Direct alignment when values are already available. Show must be called at least once before
25956      * calling this method to ensure it is initialized.
25957      * @param {Number} left The target element left position
25958      * @param {Number} top The target element top position
25959      * @param {Number} width The target element width
25960      * @param {Number} height The target element height
25961      */
25962     realign : function(l, t, w, h){
25963         if(!this.el){
25964             return;
25965         }
25966         var a = this.adjusts, d = this.el.dom, s = d.style;
25967         var iea = 0;
25968         s.left = (l+a.l)+"px";
25969         s.top = (t+a.t)+"px";
25970         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25971  
25972         if(s.width != sws || s.height != shs){
25973             s.width = sws;
25974             s.height = shs;
25975             if(!Roo.isIE){
25976                 var cn = d.childNodes;
25977                 var sww = Math.max(0, (sw-12))+"px";
25978                 cn[0].childNodes[1].style.width = sww;
25979                 cn[1].childNodes[1].style.width = sww;
25980                 cn[2].childNodes[1].style.width = sww;
25981                 cn[1].style.height = Math.max(0, (sh-12))+"px";
25982             }
25983         }
25984     },
25985
25986     /**
25987      * Hides this shadow
25988      */
25989     hide : function(){
25990         if(this.el){
25991             this.el.dom.style.display = "none";
25992             Roo.Shadow.Pool.push(this.el);
25993             delete this.el;
25994         }
25995     },
25996
25997     /**
25998      * Adjust the z-index of this shadow
25999      * @param {Number} zindex The new z-index
26000      */
26001     setZIndex : function(z){
26002         this.zIndex = z;
26003         if(this.el){
26004             this.el.setStyle("z-index", z);
26005         }
26006     }
26007 };
26008
26009 // Private utility class that manages the internal Shadow cache
26010 Roo.Shadow.Pool = function(){
26011     var p = [];
26012     var markup = Roo.isIE ?
26013                  '<div class="x-ie-shadow"></div>' :
26014                  '<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>';
26015     return {
26016         pull : function(){
26017             var sh = p.shift();
26018             if(!sh){
26019                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26020                 sh.autoBoxAdjust = false;
26021             }
26022             return sh;
26023         },
26024
26025         push : function(sh){
26026             p.push(sh);
26027         }
26028     };
26029 }();/*
26030  * Based on:
26031  * Ext JS Library 1.1.1
26032  * Copyright(c) 2006-2007, Ext JS, LLC.
26033  *
26034  * Originally Released Under LGPL - original licence link has changed is not relivant.
26035  *
26036  * Fork - LGPL
26037  * <script type="text/javascript">
26038  */
26039
26040
26041 /**
26042  * @class Roo.SplitBar
26043  * @extends Roo.util.Observable
26044  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26045  * <br><br>
26046  * Usage:
26047  * <pre><code>
26048 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26049                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26050 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26051 split.minSize = 100;
26052 split.maxSize = 600;
26053 split.animate = true;
26054 split.on('moved', splitterMoved);
26055 </code></pre>
26056  * @constructor
26057  * Create a new SplitBar
26058  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26059  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26060  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26061  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26062                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26063                         position of the SplitBar).
26064  */
26065 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26066     
26067     /** @private */
26068     this.el = Roo.get(dragElement, true);
26069     this.el.dom.unselectable = "on";
26070     /** @private */
26071     this.resizingEl = Roo.get(resizingElement, true);
26072
26073     /**
26074      * @private
26075      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26076      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26077      * @type Number
26078      */
26079     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26080     
26081     /**
26082      * The minimum size of the resizing element. (Defaults to 0)
26083      * @type Number
26084      */
26085     this.minSize = 0;
26086     
26087     /**
26088      * The maximum size of the resizing element. (Defaults to 2000)
26089      * @type Number
26090      */
26091     this.maxSize = 2000;
26092     
26093     /**
26094      * Whether to animate the transition to the new size
26095      * @type Boolean
26096      */
26097     this.animate = false;
26098     
26099     /**
26100      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26101      * @type Boolean
26102      */
26103     this.useShim = false;
26104     
26105     /** @private */
26106     this.shim = null;
26107     
26108     if(!existingProxy){
26109         /** @private */
26110         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26111     }else{
26112         this.proxy = Roo.get(existingProxy).dom;
26113     }
26114     /** @private */
26115     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26116     
26117     /** @private */
26118     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26119     
26120     /** @private */
26121     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26122     
26123     /** @private */
26124     this.dragSpecs = {};
26125     
26126     /**
26127      * @private The adapter to use to positon and resize elements
26128      */
26129     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26130     this.adapter.init(this);
26131     
26132     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26133         /** @private */
26134         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26135         this.el.addClass("x-splitbar-h");
26136     }else{
26137         /** @private */
26138         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26139         this.el.addClass("x-splitbar-v");
26140     }
26141     
26142     this.addEvents({
26143         /**
26144          * @event resize
26145          * Fires when the splitter is moved (alias for {@link #event-moved})
26146          * @param {Roo.SplitBar} this
26147          * @param {Number} newSize the new width or height
26148          */
26149         "resize" : true,
26150         /**
26151          * @event moved
26152          * Fires when the splitter is moved
26153          * @param {Roo.SplitBar} this
26154          * @param {Number} newSize the new width or height
26155          */
26156         "moved" : true,
26157         /**
26158          * @event beforeresize
26159          * Fires before the splitter is dragged
26160          * @param {Roo.SplitBar} this
26161          */
26162         "beforeresize" : true,
26163
26164         "beforeapply" : true
26165     });
26166
26167     Roo.util.Observable.call(this);
26168 };
26169
26170 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26171     onStartProxyDrag : function(x, y){
26172         this.fireEvent("beforeresize", this);
26173         if(!this.overlay){
26174             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26175             o.unselectable();
26176             o.enableDisplayMode("block");
26177             // all splitbars share the same overlay
26178             Roo.SplitBar.prototype.overlay = o;
26179         }
26180         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26181         this.overlay.show();
26182         Roo.get(this.proxy).setDisplayed("block");
26183         var size = this.adapter.getElementSize(this);
26184         this.activeMinSize = this.getMinimumSize();;
26185         this.activeMaxSize = this.getMaximumSize();;
26186         var c1 = size - this.activeMinSize;
26187         var c2 = Math.max(this.activeMaxSize - size, 0);
26188         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26189             this.dd.resetConstraints();
26190             this.dd.setXConstraint(
26191                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26192                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26193             );
26194             this.dd.setYConstraint(0, 0);
26195         }else{
26196             this.dd.resetConstraints();
26197             this.dd.setXConstraint(0, 0);
26198             this.dd.setYConstraint(
26199                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26200                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26201             );
26202          }
26203         this.dragSpecs.startSize = size;
26204         this.dragSpecs.startPoint = [x, y];
26205         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26206     },
26207     
26208     /** 
26209      * @private Called after the drag operation by the DDProxy
26210      */
26211     onEndProxyDrag : function(e){
26212         Roo.get(this.proxy).setDisplayed(false);
26213         var endPoint = Roo.lib.Event.getXY(e);
26214         if(this.overlay){
26215             this.overlay.hide();
26216         }
26217         var newSize;
26218         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26219             newSize = this.dragSpecs.startSize + 
26220                 (this.placement == Roo.SplitBar.LEFT ?
26221                     endPoint[0] - this.dragSpecs.startPoint[0] :
26222                     this.dragSpecs.startPoint[0] - endPoint[0]
26223                 );
26224         }else{
26225             newSize = this.dragSpecs.startSize + 
26226                 (this.placement == Roo.SplitBar.TOP ?
26227                     endPoint[1] - this.dragSpecs.startPoint[1] :
26228                     this.dragSpecs.startPoint[1] - endPoint[1]
26229                 );
26230         }
26231         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26232         if(newSize != this.dragSpecs.startSize){
26233             if(this.fireEvent('beforeapply', this, newSize) !== false){
26234                 this.adapter.setElementSize(this, newSize);
26235                 this.fireEvent("moved", this, newSize);
26236                 this.fireEvent("resize", this, newSize);
26237             }
26238         }
26239     },
26240     
26241     /**
26242      * Get the adapter this SplitBar uses
26243      * @return The adapter object
26244      */
26245     getAdapter : function(){
26246         return this.adapter;
26247     },
26248     
26249     /**
26250      * Set the adapter this SplitBar uses
26251      * @param {Object} adapter A SplitBar adapter object
26252      */
26253     setAdapter : function(adapter){
26254         this.adapter = adapter;
26255         this.adapter.init(this);
26256     },
26257     
26258     /**
26259      * Gets the minimum size for the resizing element
26260      * @return {Number} The minimum size
26261      */
26262     getMinimumSize : function(){
26263         return this.minSize;
26264     },
26265     
26266     /**
26267      * Sets the minimum size for the resizing element
26268      * @param {Number} minSize The minimum size
26269      */
26270     setMinimumSize : function(minSize){
26271         this.minSize = minSize;
26272     },
26273     
26274     /**
26275      * Gets the maximum size for the resizing element
26276      * @return {Number} The maximum size
26277      */
26278     getMaximumSize : function(){
26279         return this.maxSize;
26280     },
26281     
26282     /**
26283      * Sets the maximum size for the resizing element
26284      * @param {Number} maxSize The maximum size
26285      */
26286     setMaximumSize : function(maxSize){
26287         this.maxSize = maxSize;
26288     },
26289     
26290     /**
26291      * Sets the initialize size for the resizing element
26292      * @param {Number} size The initial size
26293      */
26294     setCurrentSize : function(size){
26295         var oldAnimate = this.animate;
26296         this.animate = false;
26297         this.adapter.setElementSize(this, size);
26298         this.animate = oldAnimate;
26299     },
26300     
26301     /**
26302      * Destroy this splitbar. 
26303      * @param {Boolean} removeEl True to remove the element
26304      */
26305     destroy : function(removeEl){
26306         if(this.shim){
26307             this.shim.remove();
26308         }
26309         this.dd.unreg();
26310         this.proxy.parentNode.removeChild(this.proxy);
26311         if(removeEl){
26312             this.el.remove();
26313         }
26314     }
26315 });
26316
26317 /**
26318  * @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.
26319  */
26320 Roo.SplitBar.createProxy = function(dir){
26321     var proxy = new Roo.Element(document.createElement("div"));
26322     proxy.unselectable();
26323     var cls = 'x-splitbar-proxy';
26324     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26325     document.body.appendChild(proxy.dom);
26326     return proxy.dom;
26327 };
26328
26329 /** 
26330  * @class Roo.SplitBar.BasicLayoutAdapter
26331  * Default Adapter. It assumes the splitter and resizing element are not positioned
26332  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26333  */
26334 Roo.SplitBar.BasicLayoutAdapter = function(){
26335 };
26336
26337 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26338     // do nothing for now
26339     init : function(s){
26340     
26341     },
26342     /**
26343      * Called before drag operations to get the current size of the resizing element. 
26344      * @param {Roo.SplitBar} s The SplitBar using this adapter
26345      */
26346      getElementSize : function(s){
26347         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26348             return s.resizingEl.getWidth();
26349         }else{
26350             return s.resizingEl.getHeight();
26351         }
26352     },
26353     
26354     /**
26355      * Called after drag operations to set the size of the resizing element.
26356      * @param {Roo.SplitBar} s The SplitBar using this adapter
26357      * @param {Number} newSize The new size to set
26358      * @param {Function} onComplete A function to be invoked when resizing is complete
26359      */
26360     setElementSize : function(s, newSize, onComplete){
26361         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26362             if(!s.animate){
26363                 s.resizingEl.setWidth(newSize);
26364                 if(onComplete){
26365                     onComplete(s, newSize);
26366                 }
26367             }else{
26368                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26369             }
26370         }else{
26371             
26372             if(!s.animate){
26373                 s.resizingEl.setHeight(newSize);
26374                 if(onComplete){
26375                     onComplete(s, newSize);
26376                 }
26377             }else{
26378                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26379             }
26380         }
26381     }
26382 };
26383
26384 /** 
26385  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26386  * @extends Roo.SplitBar.BasicLayoutAdapter
26387  * Adapter that  moves the splitter element to align with the resized sizing element. 
26388  * Used with an absolute positioned SplitBar.
26389  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26390  * document.body, make sure you assign an id to the body element.
26391  */
26392 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26393     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26394     this.container = Roo.get(container);
26395 };
26396
26397 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26398     init : function(s){
26399         this.basic.init(s);
26400     },
26401     
26402     getElementSize : function(s){
26403         return this.basic.getElementSize(s);
26404     },
26405     
26406     setElementSize : function(s, newSize, onComplete){
26407         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26408     },
26409     
26410     moveSplitter : function(s){
26411         var yes = Roo.SplitBar;
26412         switch(s.placement){
26413             case yes.LEFT:
26414                 s.el.setX(s.resizingEl.getRight());
26415                 break;
26416             case yes.RIGHT:
26417                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26418                 break;
26419             case yes.TOP:
26420                 s.el.setY(s.resizingEl.getBottom());
26421                 break;
26422             case yes.BOTTOM:
26423                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26424                 break;
26425         }
26426     }
26427 };
26428
26429 /**
26430  * Orientation constant - Create a vertical SplitBar
26431  * @static
26432  * @type Number
26433  */
26434 Roo.SplitBar.VERTICAL = 1;
26435
26436 /**
26437  * Orientation constant - Create a horizontal SplitBar
26438  * @static
26439  * @type Number
26440  */
26441 Roo.SplitBar.HORIZONTAL = 2;
26442
26443 /**
26444  * Placement constant - The resizing element is to the left of the splitter element
26445  * @static
26446  * @type Number
26447  */
26448 Roo.SplitBar.LEFT = 1;
26449
26450 /**
26451  * Placement constant - The resizing element is to the right of the splitter element
26452  * @static
26453  * @type Number
26454  */
26455 Roo.SplitBar.RIGHT = 2;
26456
26457 /**
26458  * Placement constant - The resizing element is positioned above the splitter element
26459  * @static
26460  * @type Number
26461  */
26462 Roo.SplitBar.TOP = 3;
26463
26464 /**
26465  * Placement constant - The resizing element is positioned under splitter element
26466  * @static
26467  * @type Number
26468  */
26469 Roo.SplitBar.BOTTOM = 4;
26470 /*
26471  * Based on:
26472  * Ext JS Library 1.1.1
26473  * Copyright(c) 2006-2007, Ext JS, LLC.
26474  *
26475  * Originally Released Under LGPL - original licence link has changed is not relivant.
26476  *
26477  * Fork - LGPL
26478  * <script type="text/javascript">
26479  */
26480
26481 /**
26482  * @class Roo.View
26483  * @extends Roo.util.Observable
26484  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26485  * This class also supports single and multi selection modes. <br>
26486  * Create a data model bound view:
26487  <pre><code>
26488  var store = new Roo.data.Store(...);
26489
26490  var view = new Roo.View({
26491     el : "my-element",
26492     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26493  
26494     singleSelect: true,
26495     selectedClass: "ydataview-selected",
26496     store: store
26497  });
26498
26499  // listen for node click?
26500  view.on("click", function(vw, index, node, e){
26501  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26502  });
26503
26504  // load XML data
26505  dataModel.load("foobar.xml");
26506  </code></pre>
26507  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26508  * <br><br>
26509  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26510  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26511  * 
26512  * Note: old style constructor is still suported (container, template, config)
26513  * 
26514  * @constructor
26515  * Create a new View
26516  * @param {Object} config The config object
26517  * 
26518  */
26519 Roo.View = function(config, depreciated_tpl, depreciated_config){
26520     
26521     this.parent = false;
26522     
26523     if (typeof(depreciated_tpl) == 'undefined') {
26524         // new way.. - universal constructor.
26525         Roo.apply(this, config);
26526         this.el  = Roo.get(this.el);
26527     } else {
26528         // old format..
26529         this.el  = Roo.get(config);
26530         this.tpl = depreciated_tpl;
26531         Roo.apply(this, depreciated_config);
26532     }
26533     this.wrapEl  = this.el.wrap().wrap();
26534     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26535     
26536     
26537     if(typeof(this.tpl) == "string"){
26538         this.tpl = new Roo.Template(this.tpl);
26539     } else {
26540         // support xtype ctors..
26541         this.tpl = new Roo.factory(this.tpl, Roo);
26542     }
26543     
26544     
26545     this.tpl.compile();
26546     
26547     /** @private */
26548     this.addEvents({
26549         /**
26550          * @event beforeclick
26551          * Fires before a click is processed. Returns false to cancel the default action.
26552          * @param {Roo.View} this
26553          * @param {Number} index The index of the target node
26554          * @param {HTMLElement} node The target node
26555          * @param {Roo.EventObject} e The raw event object
26556          */
26557             "beforeclick" : true,
26558         /**
26559          * @event click
26560          * Fires when a template node is clicked.
26561          * @param {Roo.View} this
26562          * @param {Number} index The index of the target node
26563          * @param {HTMLElement} node The target node
26564          * @param {Roo.EventObject} e The raw event object
26565          */
26566             "click" : true,
26567         /**
26568          * @event dblclick
26569          * Fires when a template node is double clicked.
26570          * @param {Roo.View} this
26571          * @param {Number} index The index of the target node
26572          * @param {HTMLElement} node The target node
26573          * @param {Roo.EventObject} e The raw event object
26574          */
26575             "dblclick" : true,
26576         /**
26577          * @event contextmenu
26578          * Fires when a template node is right clicked.
26579          * @param {Roo.View} this
26580          * @param {Number} index The index of the target node
26581          * @param {HTMLElement} node The target node
26582          * @param {Roo.EventObject} e The raw event object
26583          */
26584             "contextmenu" : true,
26585         /**
26586          * @event selectionchange
26587          * Fires when the selected nodes change.
26588          * @param {Roo.View} this
26589          * @param {Array} selections Array of the selected nodes
26590          */
26591             "selectionchange" : true,
26592     
26593         /**
26594          * @event beforeselect
26595          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26596          * @param {Roo.View} this
26597          * @param {HTMLElement} node The node to be selected
26598          * @param {Array} selections Array of currently selected nodes
26599          */
26600             "beforeselect" : true,
26601         /**
26602          * @event preparedata
26603          * Fires on every row to render, to allow you to change the data.
26604          * @param {Roo.View} this
26605          * @param {Object} data to be rendered (change this)
26606          */
26607           "preparedata" : true
26608           
26609           
26610         });
26611
26612
26613
26614     this.el.on({
26615         "click": this.onClick,
26616         "dblclick": this.onDblClick,
26617         "contextmenu": this.onContextMenu,
26618         scope:this
26619     });
26620
26621     this.selections = [];
26622     this.nodes = [];
26623     this.cmp = new Roo.CompositeElementLite([]);
26624     if(this.store){
26625         this.store = Roo.factory(this.store, Roo.data);
26626         this.setStore(this.store, true);
26627     }
26628     
26629     if ( this.footer && this.footer.xtype) {
26630            
26631          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26632         
26633         this.footer.dataSource = this.store;
26634         this.footer.container = fctr;
26635         this.footer = Roo.factory(this.footer, Roo);
26636         fctr.insertFirst(this.el);
26637         
26638         // this is a bit insane - as the paging toolbar seems to detach the el..
26639 //        dom.parentNode.parentNode.parentNode
26640          // they get detached?
26641     }
26642     
26643     
26644     Roo.View.superclass.constructor.call(this);
26645     
26646     
26647 };
26648
26649 Roo.extend(Roo.View, Roo.util.Observable, {
26650     
26651      /**
26652      * @cfg {Roo.data.Store} store Data store to load data from.
26653      */
26654     store : false,
26655     
26656     /**
26657      * @cfg {String|Roo.Element} el The container element.
26658      */
26659     el : '',
26660     
26661     /**
26662      * @cfg {String|Roo.Template} tpl The template used by this View 
26663      */
26664     tpl : false,
26665     /**
26666      * @cfg {String} dataName the named area of the template to use as the data area
26667      *                          Works with domtemplates roo-name="name"
26668      */
26669     dataName: false,
26670     /**
26671      * @cfg {String} selectedClass The css class to add to selected nodes
26672      */
26673     selectedClass : "x-view-selected",
26674      /**
26675      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26676      */
26677     emptyText : "",
26678     
26679     /**
26680      * @cfg {String} text to display on mask (default Loading)
26681      */
26682     mask : false,
26683     /**
26684      * @cfg {Boolean} multiSelect Allow multiple selection
26685      */
26686     multiSelect : false,
26687     /**
26688      * @cfg {Boolean} singleSelect Allow single selection
26689      */
26690     singleSelect:  false,
26691     
26692     /**
26693      * @cfg {Boolean} toggleSelect - selecting 
26694      */
26695     toggleSelect : false,
26696     
26697     /**
26698      * @cfg {Boolean} tickable - selecting 
26699      */
26700     tickable : false,
26701     
26702     /**
26703      * Returns the element this view is bound to.
26704      * @return {Roo.Element}
26705      */
26706     getEl : function(){
26707         return this.wrapEl;
26708     },
26709     
26710     
26711
26712     /**
26713      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26714      */
26715     refresh : function(){
26716         //Roo.log('refresh');
26717         var t = this.tpl;
26718         
26719         // if we are using something like 'domtemplate', then
26720         // the what gets used is:
26721         // t.applySubtemplate(NAME, data, wrapping data..)
26722         // the outer template then get' applied with
26723         //     the store 'extra data'
26724         // and the body get's added to the
26725         //      roo-name="data" node?
26726         //      <span class='roo-tpl-{name}'></span> ?????
26727         
26728         
26729         
26730         this.clearSelections();
26731         this.el.update("");
26732         var html = [];
26733         var records = this.store.getRange();
26734         if(records.length < 1) {
26735             
26736             // is this valid??  = should it render a template??
26737             
26738             this.el.update(this.emptyText);
26739             return;
26740         }
26741         var el = this.el;
26742         if (this.dataName) {
26743             this.el.update(t.apply(this.store.meta)); //????
26744             el = this.el.child('.roo-tpl-' + this.dataName);
26745         }
26746         
26747         for(var i = 0, len = records.length; i < len; i++){
26748             var data = this.prepareData(records[i].data, i, records[i]);
26749             this.fireEvent("preparedata", this, data, i, records[i]);
26750             
26751             var d = Roo.apply({}, data);
26752             
26753             if(this.tickable){
26754                 Roo.apply(d, {'roo-id' : Roo.id()});
26755                 
26756                 var _this = this;
26757             
26758                 Roo.each(this.parent.item, function(item){
26759                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26760                         return;
26761                     }
26762                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26763                 });
26764             }
26765             
26766             html[html.length] = Roo.util.Format.trim(
26767                 this.dataName ?
26768                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26769                     t.apply(d)
26770             );
26771         }
26772         
26773         
26774         
26775         el.update(html.join(""));
26776         this.nodes = el.dom.childNodes;
26777         this.updateIndexes(0);
26778     },
26779     
26780
26781     /**
26782      * Function to override to reformat the data that is sent to
26783      * the template for each node.
26784      * DEPRICATED - use the preparedata event handler.
26785      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26786      * a JSON object for an UpdateManager bound view).
26787      */
26788     prepareData : function(data, index, record)
26789     {
26790         this.fireEvent("preparedata", this, data, index, record);
26791         return data;
26792     },
26793
26794     onUpdate : function(ds, record){
26795         // Roo.log('on update');   
26796         this.clearSelections();
26797         var index = this.store.indexOf(record);
26798         var n = this.nodes[index];
26799         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26800         n.parentNode.removeChild(n);
26801         this.updateIndexes(index, index);
26802     },
26803
26804     
26805     
26806 // --------- FIXME     
26807     onAdd : function(ds, records, index)
26808     {
26809         //Roo.log(['on Add', ds, records, index] );        
26810         this.clearSelections();
26811         if(this.nodes.length == 0){
26812             this.refresh();
26813             return;
26814         }
26815         var n = this.nodes[index];
26816         for(var i = 0, len = records.length; i < len; i++){
26817             var d = this.prepareData(records[i].data, i, records[i]);
26818             if(n){
26819                 this.tpl.insertBefore(n, d);
26820             }else{
26821                 
26822                 this.tpl.append(this.el, d);
26823             }
26824         }
26825         this.updateIndexes(index);
26826     },
26827
26828     onRemove : function(ds, record, index){
26829        // Roo.log('onRemove');
26830         this.clearSelections();
26831         var el = this.dataName  ?
26832             this.el.child('.roo-tpl-' + this.dataName) :
26833             this.el; 
26834         
26835         el.dom.removeChild(this.nodes[index]);
26836         this.updateIndexes(index);
26837     },
26838
26839     /**
26840      * Refresh an individual node.
26841      * @param {Number} index
26842      */
26843     refreshNode : function(index){
26844         this.onUpdate(this.store, this.store.getAt(index));
26845     },
26846
26847     updateIndexes : function(startIndex, endIndex){
26848         var ns = this.nodes;
26849         startIndex = startIndex || 0;
26850         endIndex = endIndex || ns.length - 1;
26851         for(var i = startIndex; i <= endIndex; i++){
26852             ns[i].nodeIndex = i;
26853         }
26854     },
26855
26856     /**
26857      * Changes the data store this view uses and refresh the view.
26858      * @param {Store} store
26859      */
26860     setStore : function(store, initial){
26861         if(!initial && this.store){
26862             this.store.un("datachanged", this.refresh);
26863             this.store.un("add", this.onAdd);
26864             this.store.un("remove", this.onRemove);
26865             this.store.un("update", this.onUpdate);
26866             this.store.un("clear", this.refresh);
26867             this.store.un("beforeload", this.onBeforeLoad);
26868             this.store.un("load", this.onLoad);
26869             this.store.un("loadexception", this.onLoad);
26870         }
26871         if(store){
26872           
26873             store.on("datachanged", this.refresh, this);
26874             store.on("add", this.onAdd, this);
26875             store.on("remove", this.onRemove, this);
26876             store.on("update", this.onUpdate, this);
26877             store.on("clear", this.refresh, this);
26878             store.on("beforeload", this.onBeforeLoad, this);
26879             store.on("load", this.onLoad, this);
26880             store.on("loadexception", this.onLoad, this);
26881         }
26882         
26883         if(store){
26884             this.refresh();
26885         }
26886     },
26887     /**
26888      * onbeforeLoad - masks the loading area.
26889      *
26890      */
26891     onBeforeLoad : function(store,opts)
26892     {
26893          //Roo.log('onBeforeLoad');   
26894         if (!opts.add) {
26895             this.el.update("");
26896         }
26897         this.el.mask(this.mask ? this.mask : "Loading" ); 
26898     },
26899     onLoad : function ()
26900     {
26901         this.el.unmask();
26902     },
26903     
26904
26905     /**
26906      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26907      * @param {HTMLElement} node
26908      * @return {HTMLElement} The template node
26909      */
26910     findItemFromChild : function(node){
26911         var el = this.dataName  ?
26912             this.el.child('.roo-tpl-' + this.dataName,true) :
26913             this.el.dom; 
26914         
26915         if(!node || node.parentNode == el){
26916                     return node;
26917             }
26918             var p = node.parentNode;
26919             while(p && p != el){
26920             if(p.parentNode == el){
26921                 return p;
26922             }
26923             p = p.parentNode;
26924         }
26925             return null;
26926     },
26927
26928     /** @ignore */
26929     onClick : function(e){
26930         var item = this.findItemFromChild(e.getTarget());
26931         if(item){
26932             var index = this.indexOf(item);
26933             if(this.onItemClick(item, index, e) !== false){
26934                 this.fireEvent("click", this, index, item, e);
26935             }
26936         }else{
26937             this.clearSelections();
26938         }
26939     },
26940
26941     /** @ignore */
26942     onContextMenu : function(e){
26943         var item = this.findItemFromChild(e.getTarget());
26944         if(item){
26945             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26946         }
26947     },
26948
26949     /** @ignore */
26950     onDblClick : function(e){
26951         var item = this.findItemFromChild(e.getTarget());
26952         if(item){
26953             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26954         }
26955     },
26956
26957     onItemClick : function(item, index, e)
26958     {
26959         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26960             return false;
26961         }
26962         if (this.toggleSelect) {
26963             var m = this.isSelected(item) ? 'unselect' : 'select';
26964             //Roo.log(m);
26965             var _t = this;
26966             _t[m](item, true, false);
26967             return true;
26968         }
26969         if(this.multiSelect || this.singleSelect){
26970             if(this.multiSelect && e.shiftKey && this.lastSelection){
26971                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26972             }else{
26973                 this.select(item, this.multiSelect && e.ctrlKey);
26974                 this.lastSelection = item;
26975             }
26976             
26977             if(!this.tickable){
26978                 e.preventDefault();
26979             }
26980             
26981         }
26982         return true;
26983     },
26984
26985     /**
26986      * Get the number of selected nodes.
26987      * @return {Number}
26988      */
26989     getSelectionCount : function(){
26990         return this.selections.length;
26991     },
26992
26993     /**
26994      * Get the currently selected nodes.
26995      * @return {Array} An array of HTMLElements
26996      */
26997     getSelectedNodes : function(){
26998         return this.selections;
26999     },
27000
27001     /**
27002      * Get the indexes of the selected nodes.
27003      * @return {Array}
27004      */
27005     getSelectedIndexes : function(){
27006         var indexes = [], s = this.selections;
27007         for(var i = 0, len = s.length; i < len; i++){
27008             indexes.push(s[i].nodeIndex);
27009         }
27010         return indexes;
27011     },
27012
27013     /**
27014      * Clear all selections
27015      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27016      */
27017     clearSelections : function(suppressEvent){
27018         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27019             this.cmp.elements = this.selections;
27020             this.cmp.removeClass(this.selectedClass);
27021             this.selections = [];
27022             if(!suppressEvent){
27023                 this.fireEvent("selectionchange", this, this.selections);
27024             }
27025         }
27026     },
27027
27028     /**
27029      * Returns true if the passed node is selected
27030      * @param {HTMLElement/Number} node The node or node index
27031      * @return {Boolean}
27032      */
27033     isSelected : function(node){
27034         var s = this.selections;
27035         if(s.length < 1){
27036             return false;
27037         }
27038         node = this.getNode(node);
27039         return s.indexOf(node) !== -1;
27040     },
27041
27042     /**
27043      * Selects nodes.
27044      * @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
27045      * @param {Boolean} keepExisting (optional) true to keep existing selections
27046      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27047      */
27048     select : function(nodeInfo, keepExisting, suppressEvent){
27049         if(nodeInfo instanceof Array){
27050             if(!keepExisting){
27051                 this.clearSelections(true);
27052             }
27053             for(var i = 0, len = nodeInfo.length; i < len; i++){
27054                 this.select(nodeInfo[i], true, true);
27055             }
27056             return;
27057         } 
27058         var node = this.getNode(nodeInfo);
27059         if(!node || this.isSelected(node)){
27060             return; // already selected.
27061         }
27062         if(!keepExisting){
27063             this.clearSelections(true);
27064         }
27065         
27066         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27067             Roo.fly(node).addClass(this.selectedClass);
27068             this.selections.push(node);
27069             if(!suppressEvent){
27070                 this.fireEvent("selectionchange", this, this.selections);
27071             }
27072         }
27073         
27074         
27075     },
27076       /**
27077      * Unselects nodes.
27078      * @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
27079      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27080      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27081      */
27082     unselect : function(nodeInfo, keepExisting, suppressEvent)
27083     {
27084         if(nodeInfo instanceof Array){
27085             Roo.each(this.selections, function(s) {
27086                 this.unselect(s, nodeInfo);
27087             }, this);
27088             return;
27089         }
27090         var node = this.getNode(nodeInfo);
27091         if(!node || !this.isSelected(node)){
27092             //Roo.log("not selected");
27093             return; // not selected.
27094         }
27095         // fireevent???
27096         var ns = [];
27097         Roo.each(this.selections, function(s) {
27098             if (s == node ) {
27099                 Roo.fly(node).removeClass(this.selectedClass);
27100
27101                 return;
27102             }
27103             ns.push(s);
27104         },this);
27105         
27106         this.selections= ns;
27107         this.fireEvent("selectionchange", this, this.selections);
27108     },
27109
27110     /**
27111      * Gets a template node.
27112      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27113      * @return {HTMLElement} The node or null if it wasn't found
27114      */
27115     getNode : function(nodeInfo){
27116         if(typeof nodeInfo == "string"){
27117             return document.getElementById(nodeInfo);
27118         }else if(typeof nodeInfo == "number"){
27119             return this.nodes[nodeInfo];
27120         }
27121         return nodeInfo;
27122     },
27123
27124     /**
27125      * Gets a range template nodes.
27126      * @param {Number} startIndex
27127      * @param {Number} endIndex
27128      * @return {Array} An array of nodes
27129      */
27130     getNodes : function(start, end){
27131         var ns = this.nodes;
27132         start = start || 0;
27133         end = typeof end == "undefined" ? ns.length - 1 : end;
27134         var nodes = [];
27135         if(start <= end){
27136             for(var i = start; i <= end; i++){
27137                 nodes.push(ns[i]);
27138             }
27139         } else{
27140             for(var i = start; i >= end; i--){
27141                 nodes.push(ns[i]);
27142             }
27143         }
27144         return nodes;
27145     },
27146
27147     /**
27148      * Finds the index of the passed node
27149      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27150      * @return {Number} The index of the node or -1
27151      */
27152     indexOf : function(node){
27153         node = this.getNode(node);
27154         if(typeof node.nodeIndex == "number"){
27155             return node.nodeIndex;
27156         }
27157         var ns = this.nodes;
27158         for(var i = 0, len = ns.length; i < len; i++){
27159             if(ns[i] == node){
27160                 return i;
27161             }
27162         }
27163         return -1;
27164     }
27165 });
27166 /*
27167  * Based on:
27168  * Ext JS Library 1.1.1
27169  * Copyright(c) 2006-2007, Ext JS, LLC.
27170  *
27171  * Originally Released Under LGPL - original licence link has changed is not relivant.
27172  *
27173  * Fork - LGPL
27174  * <script type="text/javascript">
27175  */
27176
27177 /**
27178  * @class Roo.JsonView
27179  * @extends Roo.View
27180  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27181 <pre><code>
27182 var view = new Roo.JsonView({
27183     container: "my-element",
27184     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27185     multiSelect: true, 
27186     jsonRoot: "data" 
27187 });
27188
27189 // listen for node click?
27190 view.on("click", function(vw, index, node, e){
27191     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27192 });
27193
27194 // direct load of JSON data
27195 view.load("foobar.php");
27196
27197 // Example from my blog list
27198 var tpl = new Roo.Template(
27199     '&lt;div class="entry"&gt;' +
27200     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27201     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27202     "&lt;/div&gt;&lt;hr /&gt;"
27203 );
27204
27205 var moreView = new Roo.JsonView({
27206     container :  "entry-list", 
27207     template : tpl,
27208     jsonRoot: "posts"
27209 });
27210 moreView.on("beforerender", this.sortEntries, this);
27211 moreView.load({
27212     url: "/blog/get-posts.php",
27213     params: "allposts=true",
27214     text: "Loading Blog Entries..."
27215 });
27216 </code></pre>
27217
27218 * Note: old code is supported with arguments : (container, template, config)
27219
27220
27221  * @constructor
27222  * Create a new JsonView
27223  * 
27224  * @param {Object} config The config object
27225  * 
27226  */
27227 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27228     
27229     
27230     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27231
27232     var um = this.el.getUpdateManager();
27233     um.setRenderer(this);
27234     um.on("update", this.onLoad, this);
27235     um.on("failure", this.onLoadException, this);
27236
27237     /**
27238      * @event beforerender
27239      * Fires before rendering of the downloaded JSON data.
27240      * @param {Roo.JsonView} this
27241      * @param {Object} data The JSON data loaded
27242      */
27243     /**
27244      * @event load
27245      * Fires when data is loaded.
27246      * @param {Roo.JsonView} this
27247      * @param {Object} data The JSON data loaded
27248      * @param {Object} response The raw Connect response object
27249      */
27250     /**
27251      * @event loadexception
27252      * Fires when loading fails.
27253      * @param {Roo.JsonView} this
27254      * @param {Object} response The raw Connect response object
27255      */
27256     this.addEvents({
27257         'beforerender' : true,
27258         'load' : true,
27259         'loadexception' : true
27260     });
27261 };
27262 Roo.extend(Roo.JsonView, Roo.View, {
27263     /**
27264      * @type {String} The root property in the loaded JSON object that contains the data
27265      */
27266     jsonRoot : "",
27267
27268     /**
27269      * Refreshes the view.
27270      */
27271     refresh : function(){
27272         this.clearSelections();
27273         this.el.update("");
27274         var html = [];
27275         var o = this.jsonData;
27276         if(o && o.length > 0){
27277             for(var i = 0, len = o.length; i < len; i++){
27278                 var data = this.prepareData(o[i], i, o);
27279                 html[html.length] = this.tpl.apply(data);
27280             }
27281         }else{
27282             html.push(this.emptyText);
27283         }
27284         this.el.update(html.join(""));
27285         this.nodes = this.el.dom.childNodes;
27286         this.updateIndexes(0);
27287     },
27288
27289     /**
27290      * 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.
27291      * @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:
27292      <pre><code>
27293      view.load({
27294          url: "your-url.php",
27295          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27296          callback: yourFunction,
27297          scope: yourObject, //(optional scope)
27298          discardUrl: false,
27299          nocache: false,
27300          text: "Loading...",
27301          timeout: 30,
27302          scripts: false
27303      });
27304      </code></pre>
27305      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27306      * 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.
27307      * @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}
27308      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27309      * @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.
27310      */
27311     load : function(){
27312         var um = this.el.getUpdateManager();
27313         um.update.apply(um, arguments);
27314     },
27315
27316     // note - render is a standard framework call...
27317     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27318     render : function(el, response){
27319         
27320         this.clearSelections();
27321         this.el.update("");
27322         var o;
27323         try{
27324             if (response != '') {
27325                 o = Roo.util.JSON.decode(response.responseText);
27326                 if(this.jsonRoot){
27327                     
27328                     o = o[this.jsonRoot];
27329                 }
27330             }
27331         } catch(e){
27332         }
27333         /**
27334          * The current JSON data or null
27335          */
27336         this.jsonData = o;
27337         this.beforeRender();
27338         this.refresh();
27339     },
27340
27341 /**
27342  * Get the number of records in the current JSON dataset
27343  * @return {Number}
27344  */
27345     getCount : function(){
27346         return this.jsonData ? this.jsonData.length : 0;
27347     },
27348
27349 /**
27350  * Returns the JSON object for the specified node(s)
27351  * @param {HTMLElement/Array} node The node or an array of nodes
27352  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27353  * you get the JSON object for the node
27354  */
27355     getNodeData : function(node){
27356         if(node instanceof Array){
27357             var data = [];
27358             for(var i = 0, len = node.length; i < len; i++){
27359                 data.push(this.getNodeData(node[i]));
27360             }
27361             return data;
27362         }
27363         return this.jsonData[this.indexOf(node)] || null;
27364     },
27365
27366     beforeRender : function(){
27367         this.snapshot = this.jsonData;
27368         if(this.sortInfo){
27369             this.sort.apply(this, this.sortInfo);
27370         }
27371         this.fireEvent("beforerender", this, this.jsonData);
27372     },
27373
27374     onLoad : function(el, o){
27375         this.fireEvent("load", this, this.jsonData, o);
27376     },
27377
27378     onLoadException : function(el, o){
27379         this.fireEvent("loadexception", this, o);
27380     },
27381
27382 /**
27383  * Filter the data by a specific property.
27384  * @param {String} property A property on your JSON objects
27385  * @param {String/RegExp} value Either string that the property values
27386  * should start with, or a RegExp to test against the property
27387  */
27388     filter : function(property, value){
27389         if(this.jsonData){
27390             var data = [];
27391             var ss = this.snapshot;
27392             if(typeof value == "string"){
27393                 var vlen = value.length;
27394                 if(vlen == 0){
27395                     this.clearFilter();
27396                     return;
27397                 }
27398                 value = value.toLowerCase();
27399                 for(var i = 0, len = ss.length; i < len; i++){
27400                     var o = ss[i];
27401                     if(o[property].substr(0, vlen).toLowerCase() == value){
27402                         data.push(o);
27403                     }
27404                 }
27405             } else if(value.exec){ // regex?
27406                 for(var i = 0, len = ss.length; i < len; i++){
27407                     var o = ss[i];
27408                     if(value.test(o[property])){
27409                         data.push(o);
27410                     }
27411                 }
27412             } else{
27413                 return;
27414             }
27415             this.jsonData = data;
27416             this.refresh();
27417         }
27418     },
27419
27420 /**
27421  * Filter by a function. The passed function will be called with each
27422  * object in the current dataset. If the function returns true the value is kept,
27423  * otherwise it is filtered.
27424  * @param {Function} fn
27425  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27426  */
27427     filterBy : function(fn, scope){
27428         if(this.jsonData){
27429             var data = [];
27430             var ss = this.snapshot;
27431             for(var i = 0, len = ss.length; i < len; i++){
27432                 var o = ss[i];
27433                 if(fn.call(scope || this, o)){
27434                     data.push(o);
27435                 }
27436             }
27437             this.jsonData = data;
27438             this.refresh();
27439         }
27440     },
27441
27442 /**
27443  * Clears the current filter.
27444  */
27445     clearFilter : function(){
27446         if(this.snapshot && this.jsonData != this.snapshot){
27447             this.jsonData = this.snapshot;
27448             this.refresh();
27449         }
27450     },
27451
27452
27453 /**
27454  * Sorts the data for this view and refreshes it.
27455  * @param {String} property A property on your JSON objects to sort on
27456  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27457  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27458  */
27459     sort : function(property, dir, sortType){
27460         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27461         if(this.jsonData){
27462             var p = property;
27463             var dsc = dir && dir.toLowerCase() == "desc";
27464             var f = function(o1, o2){
27465                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27466                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27467                 ;
27468                 if(v1 < v2){
27469                     return dsc ? +1 : -1;
27470                 } else if(v1 > v2){
27471                     return dsc ? -1 : +1;
27472                 } else{
27473                     return 0;
27474                 }
27475             };
27476             this.jsonData.sort(f);
27477             this.refresh();
27478             if(this.jsonData != this.snapshot){
27479                 this.snapshot.sort(f);
27480             }
27481         }
27482     }
27483 });/*
27484  * Based on:
27485  * Ext JS Library 1.1.1
27486  * Copyright(c) 2006-2007, Ext JS, LLC.
27487  *
27488  * Originally Released Under LGPL - original licence link has changed is not relivant.
27489  *
27490  * Fork - LGPL
27491  * <script type="text/javascript">
27492  */
27493  
27494
27495 /**
27496  * @class Roo.ColorPalette
27497  * @extends Roo.Component
27498  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27499  * Here's an example of typical usage:
27500  * <pre><code>
27501 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27502 cp.render('my-div');
27503
27504 cp.on('select', function(palette, selColor){
27505     // do something with selColor
27506 });
27507 </code></pre>
27508  * @constructor
27509  * Create a new ColorPalette
27510  * @param {Object} config The config object
27511  */
27512 Roo.ColorPalette = function(config){
27513     Roo.ColorPalette.superclass.constructor.call(this, config);
27514     this.addEvents({
27515         /**
27516              * @event select
27517              * Fires when a color is selected
27518              * @param {ColorPalette} this
27519              * @param {String} color The 6-digit color hex code (without the # symbol)
27520              */
27521         select: true
27522     });
27523
27524     if(this.handler){
27525         this.on("select", this.handler, this.scope, true);
27526     }
27527 };
27528 Roo.extend(Roo.ColorPalette, Roo.Component, {
27529     /**
27530      * @cfg {String} itemCls
27531      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27532      */
27533     itemCls : "x-color-palette",
27534     /**
27535      * @cfg {String} value
27536      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27537      * the hex codes are case-sensitive.
27538      */
27539     value : null,
27540     clickEvent:'click',
27541     // private
27542     ctype: "Roo.ColorPalette",
27543
27544     /**
27545      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27546      */
27547     allowReselect : false,
27548
27549     /**
27550      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27551      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27552      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27553      * of colors with the width setting until the box is symmetrical.</p>
27554      * <p>You can override individual colors if needed:</p>
27555      * <pre><code>
27556 var cp = new Roo.ColorPalette();
27557 cp.colors[0] = "FF0000";  // change the first box to red
27558 </code></pre>
27559
27560 Or you can provide a custom array of your own for complete control:
27561 <pre><code>
27562 var cp = new Roo.ColorPalette();
27563 cp.colors = ["000000", "993300", "333300"];
27564 </code></pre>
27565      * @type Array
27566      */
27567     colors : [
27568         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27569         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27570         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27571         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27572         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27573     ],
27574
27575     // private
27576     onRender : function(container, position){
27577         var t = new Roo.MasterTemplate(
27578             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27579         );
27580         var c = this.colors;
27581         for(var i = 0, len = c.length; i < len; i++){
27582             t.add([c[i]]);
27583         }
27584         var el = document.createElement("div");
27585         el.className = this.itemCls;
27586         t.overwrite(el);
27587         container.dom.insertBefore(el, position);
27588         this.el = Roo.get(el);
27589         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27590         if(this.clickEvent != 'click'){
27591             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27592         }
27593     },
27594
27595     // private
27596     afterRender : function(){
27597         Roo.ColorPalette.superclass.afterRender.call(this);
27598         if(this.value){
27599             var s = this.value;
27600             this.value = null;
27601             this.select(s);
27602         }
27603     },
27604
27605     // private
27606     handleClick : function(e, t){
27607         e.preventDefault();
27608         if(!this.disabled){
27609             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27610             this.select(c.toUpperCase());
27611         }
27612     },
27613
27614     /**
27615      * Selects the specified color in the palette (fires the select event)
27616      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27617      */
27618     select : function(color){
27619         color = color.replace("#", "");
27620         if(color != this.value || this.allowReselect){
27621             var el = this.el;
27622             if(this.value){
27623                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27624             }
27625             el.child("a.color-"+color).addClass("x-color-palette-sel");
27626             this.value = color;
27627             this.fireEvent("select", this, color);
27628         }
27629     }
27630 });/*
27631  * Based on:
27632  * Ext JS Library 1.1.1
27633  * Copyright(c) 2006-2007, Ext JS, LLC.
27634  *
27635  * Originally Released Under LGPL - original licence link has changed is not relivant.
27636  *
27637  * Fork - LGPL
27638  * <script type="text/javascript">
27639  */
27640  
27641 /**
27642  * @class Roo.DatePicker
27643  * @extends Roo.Component
27644  * Simple date picker class.
27645  * @constructor
27646  * Create a new DatePicker
27647  * @param {Object} config The config object
27648  */
27649 Roo.DatePicker = function(config){
27650     Roo.DatePicker.superclass.constructor.call(this, config);
27651
27652     this.value = config && config.value ?
27653                  config.value.clearTime() : new Date().clearTime();
27654
27655     this.addEvents({
27656         /**
27657              * @event select
27658              * Fires when a date is selected
27659              * @param {DatePicker} this
27660              * @param {Date} date The selected date
27661              */
27662         'select': true,
27663         /**
27664              * @event monthchange
27665              * Fires when the displayed month changes 
27666              * @param {DatePicker} this
27667              * @param {Date} date The selected month
27668              */
27669         'monthchange': true
27670     });
27671
27672     if(this.handler){
27673         this.on("select", this.handler,  this.scope || this);
27674     }
27675     // build the disabledDatesRE
27676     if(!this.disabledDatesRE && this.disabledDates){
27677         var dd = this.disabledDates;
27678         var re = "(?:";
27679         for(var i = 0; i < dd.length; i++){
27680             re += dd[i];
27681             if(i != dd.length-1) {
27682                 re += "|";
27683             }
27684         }
27685         this.disabledDatesRE = new RegExp(re + ")");
27686     }
27687 };
27688
27689 Roo.extend(Roo.DatePicker, Roo.Component, {
27690     /**
27691      * @cfg {String} todayText
27692      * The text to display on the button that selects the current date (defaults to "Today")
27693      */
27694     todayText : "Today",
27695     /**
27696      * @cfg {String} okText
27697      * The text to display on the ok button
27698      */
27699     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27700     /**
27701      * @cfg {String} cancelText
27702      * The text to display on the cancel button
27703      */
27704     cancelText : "Cancel",
27705     /**
27706      * @cfg {String} todayTip
27707      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27708      */
27709     todayTip : "{0} (Spacebar)",
27710     /**
27711      * @cfg {Date} minDate
27712      * Minimum allowable date (JavaScript date object, defaults to null)
27713      */
27714     minDate : null,
27715     /**
27716      * @cfg {Date} maxDate
27717      * Maximum allowable date (JavaScript date object, defaults to null)
27718      */
27719     maxDate : null,
27720     /**
27721      * @cfg {String} minText
27722      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27723      */
27724     minText : "This date is before the minimum date",
27725     /**
27726      * @cfg {String} maxText
27727      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27728      */
27729     maxText : "This date is after the maximum date",
27730     /**
27731      * @cfg {String} format
27732      * The default date format string which can be overriden for localization support.  The format must be
27733      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27734      */
27735     format : "m/d/y",
27736     /**
27737      * @cfg {Array} disabledDays
27738      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27739      */
27740     disabledDays : null,
27741     /**
27742      * @cfg {String} disabledDaysText
27743      * The tooltip to display when the date falls on a disabled day (defaults to "")
27744      */
27745     disabledDaysText : "",
27746     /**
27747      * @cfg {RegExp} disabledDatesRE
27748      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27749      */
27750     disabledDatesRE : null,
27751     /**
27752      * @cfg {String} disabledDatesText
27753      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27754      */
27755     disabledDatesText : "",
27756     /**
27757      * @cfg {Boolean} constrainToViewport
27758      * True to constrain the date picker to the viewport (defaults to true)
27759      */
27760     constrainToViewport : true,
27761     /**
27762      * @cfg {Array} monthNames
27763      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27764      */
27765     monthNames : Date.monthNames,
27766     /**
27767      * @cfg {Array} dayNames
27768      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27769      */
27770     dayNames : Date.dayNames,
27771     /**
27772      * @cfg {String} nextText
27773      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27774      */
27775     nextText: 'Next Month (Control+Right)',
27776     /**
27777      * @cfg {String} prevText
27778      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27779      */
27780     prevText: 'Previous Month (Control+Left)',
27781     /**
27782      * @cfg {String} monthYearText
27783      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27784      */
27785     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27786     /**
27787      * @cfg {Number} startDay
27788      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27789      */
27790     startDay : 0,
27791     /**
27792      * @cfg {Bool} showClear
27793      * Show a clear button (usefull for date form elements that can be blank.)
27794      */
27795     
27796     showClear: false,
27797     
27798     /**
27799      * Sets the value of the date field
27800      * @param {Date} value The date to set
27801      */
27802     setValue : function(value){
27803         var old = this.value;
27804         
27805         if (typeof(value) == 'string') {
27806          
27807             value = Date.parseDate(value, this.format);
27808         }
27809         if (!value) {
27810             value = new Date();
27811         }
27812         
27813         this.value = value.clearTime(true);
27814         if(this.el){
27815             this.update(this.value);
27816         }
27817     },
27818
27819     /**
27820      * Gets the current selected value of the date field
27821      * @return {Date} The selected date
27822      */
27823     getValue : function(){
27824         return this.value;
27825     },
27826
27827     // private
27828     focus : function(){
27829         if(this.el){
27830             this.update(this.activeDate);
27831         }
27832     },
27833
27834     // privateval
27835     onRender : function(container, position){
27836         
27837         var m = [
27838              '<table cellspacing="0">',
27839                 '<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>',
27840                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27841         var dn = this.dayNames;
27842         for(var i = 0; i < 7; i++){
27843             var d = this.startDay+i;
27844             if(d > 6){
27845                 d = d-7;
27846             }
27847             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27848         }
27849         m[m.length] = "</tr></thead><tbody><tr>";
27850         for(var i = 0; i < 42; i++) {
27851             if(i % 7 == 0 && i != 0){
27852                 m[m.length] = "</tr><tr>";
27853             }
27854             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27855         }
27856         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27857             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27858
27859         var el = document.createElement("div");
27860         el.className = "x-date-picker";
27861         el.innerHTML = m.join("");
27862
27863         container.dom.insertBefore(el, position);
27864
27865         this.el = Roo.get(el);
27866         this.eventEl = Roo.get(el.firstChild);
27867
27868         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27869             handler: this.showPrevMonth,
27870             scope: this,
27871             preventDefault:true,
27872             stopDefault:true
27873         });
27874
27875         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27876             handler: this.showNextMonth,
27877             scope: this,
27878             preventDefault:true,
27879             stopDefault:true
27880         });
27881
27882         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27883
27884         this.monthPicker = this.el.down('div.x-date-mp');
27885         this.monthPicker.enableDisplayMode('block');
27886         
27887         var kn = new Roo.KeyNav(this.eventEl, {
27888             "left" : function(e){
27889                 e.ctrlKey ?
27890                     this.showPrevMonth() :
27891                     this.update(this.activeDate.add("d", -1));
27892             },
27893
27894             "right" : function(e){
27895                 e.ctrlKey ?
27896                     this.showNextMonth() :
27897                     this.update(this.activeDate.add("d", 1));
27898             },
27899
27900             "up" : function(e){
27901                 e.ctrlKey ?
27902                     this.showNextYear() :
27903                     this.update(this.activeDate.add("d", -7));
27904             },
27905
27906             "down" : function(e){
27907                 e.ctrlKey ?
27908                     this.showPrevYear() :
27909                     this.update(this.activeDate.add("d", 7));
27910             },
27911
27912             "pageUp" : function(e){
27913                 this.showNextMonth();
27914             },
27915
27916             "pageDown" : function(e){
27917                 this.showPrevMonth();
27918             },
27919
27920             "enter" : function(e){
27921                 e.stopPropagation();
27922                 return true;
27923             },
27924
27925             scope : this
27926         });
27927
27928         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27929
27930         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27931
27932         this.el.unselectable();
27933         
27934         this.cells = this.el.select("table.x-date-inner tbody td");
27935         this.textNodes = this.el.query("table.x-date-inner tbody span");
27936
27937         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27938             text: "&#160;",
27939             tooltip: this.monthYearText
27940         });
27941
27942         this.mbtn.on('click', this.showMonthPicker, this);
27943         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27944
27945
27946         var today = (new Date()).dateFormat(this.format);
27947         
27948         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27949         if (this.showClear) {
27950             baseTb.add( new Roo.Toolbar.Fill());
27951         }
27952         baseTb.add({
27953             text: String.format(this.todayText, today),
27954             tooltip: String.format(this.todayTip, today),
27955             handler: this.selectToday,
27956             scope: this
27957         });
27958         
27959         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27960             
27961         //});
27962         if (this.showClear) {
27963             
27964             baseTb.add( new Roo.Toolbar.Fill());
27965             baseTb.add({
27966                 text: '&#160;',
27967                 cls: 'x-btn-icon x-btn-clear',
27968                 handler: function() {
27969                     //this.value = '';
27970                     this.fireEvent("select", this, '');
27971                 },
27972                 scope: this
27973             });
27974         }
27975         
27976         
27977         if(Roo.isIE){
27978             this.el.repaint();
27979         }
27980         this.update(this.value);
27981     },
27982
27983     createMonthPicker : function(){
27984         if(!this.monthPicker.dom.firstChild){
27985             var buf = ['<table border="0" cellspacing="0">'];
27986             for(var i = 0; i < 6; i++){
27987                 buf.push(
27988                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
27989                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
27990                     i == 0 ?
27991                     '<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>' :
27992                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
27993                 );
27994             }
27995             buf.push(
27996                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
27997                     this.okText,
27998                     '</button><button type="button" class="x-date-mp-cancel">',
27999                     this.cancelText,
28000                     '</button></td></tr>',
28001                 '</table>'
28002             );
28003             this.monthPicker.update(buf.join(''));
28004             this.monthPicker.on('click', this.onMonthClick, this);
28005             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28006
28007             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28008             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28009
28010             this.mpMonths.each(function(m, a, i){
28011                 i += 1;
28012                 if((i%2) == 0){
28013                     m.dom.xmonth = 5 + Math.round(i * .5);
28014                 }else{
28015                     m.dom.xmonth = Math.round((i-1) * .5);
28016                 }
28017             });
28018         }
28019     },
28020
28021     showMonthPicker : function(){
28022         this.createMonthPicker();
28023         var size = this.el.getSize();
28024         this.monthPicker.setSize(size);
28025         this.monthPicker.child('table').setSize(size);
28026
28027         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28028         this.updateMPMonth(this.mpSelMonth);
28029         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28030         this.updateMPYear(this.mpSelYear);
28031
28032         this.monthPicker.slideIn('t', {duration:.2});
28033     },
28034
28035     updateMPYear : function(y){
28036         this.mpyear = y;
28037         var ys = this.mpYears.elements;
28038         for(var i = 1; i <= 10; i++){
28039             var td = ys[i-1], y2;
28040             if((i%2) == 0){
28041                 y2 = y + Math.round(i * .5);
28042                 td.firstChild.innerHTML = y2;
28043                 td.xyear = y2;
28044             }else{
28045                 y2 = y - (5-Math.round(i * .5));
28046                 td.firstChild.innerHTML = y2;
28047                 td.xyear = y2;
28048             }
28049             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28050         }
28051     },
28052
28053     updateMPMonth : function(sm){
28054         this.mpMonths.each(function(m, a, i){
28055             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28056         });
28057     },
28058
28059     selectMPMonth: function(m){
28060         
28061     },
28062
28063     onMonthClick : function(e, t){
28064         e.stopEvent();
28065         var el = new Roo.Element(t), pn;
28066         if(el.is('button.x-date-mp-cancel')){
28067             this.hideMonthPicker();
28068         }
28069         else if(el.is('button.x-date-mp-ok')){
28070             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28071             this.hideMonthPicker();
28072         }
28073         else if(pn = el.up('td.x-date-mp-month', 2)){
28074             this.mpMonths.removeClass('x-date-mp-sel');
28075             pn.addClass('x-date-mp-sel');
28076             this.mpSelMonth = pn.dom.xmonth;
28077         }
28078         else if(pn = el.up('td.x-date-mp-year', 2)){
28079             this.mpYears.removeClass('x-date-mp-sel');
28080             pn.addClass('x-date-mp-sel');
28081             this.mpSelYear = pn.dom.xyear;
28082         }
28083         else if(el.is('a.x-date-mp-prev')){
28084             this.updateMPYear(this.mpyear-10);
28085         }
28086         else if(el.is('a.x-date-mp-next')){
28087             this.updateMPYear(this.mpyear+10);
28088         }
28089     },
28090
28091     onMonthDblClick : function(e, t){
28092         e.stopEvent();
28093         var el = new Roo.Element(t), pn;
28094         if(pn = el.up('td.x-date-mp-month', 2)){
28095             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28096             this.hideMonthPicker();
28097         }
28098         else if(pn = el.up('td.x-date-mp-year', 2)){
28099             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28100             this.hideMonthPicker();
28101         }
28102     },
28103
28104     hideMonthPicker : function(disableAnim){
28105         if(this.monthPicker){
28106             if(disableAnim === true){
28107                 this.monthPicker.hide();
28108             }else{
28109                 this.monthPicker.slideOut('t', {duration:.2});
28110             }
28111         }
28112     },
28113
28114     // private
28115     showPrevMonth : function(e){
28116         this.update(this.activeDate.add("mo", -1));
28117     },
28118
28119     // private
28120     showNextMonth : function(e){
28121         this.update(this.activeDate.add("mo", 1));
28122     },
28123
28124     // private
28125     showPrevYear : function(){
28126         this.update(this.activeDate.add("y", -1));
28127     },
28128
28129     // private
28130     showNextYear : function(){
28131         this.update(this.activeDate.add("y", 1));
28132     },
28133
28134     // private
28135     handleMouseWheel : function(e){
28136         var delta = e.getWheelDelta();
28137         if(delta > 0){
28138             this.showPrevMonth();
28139             e.stopEvent();
28140         } else if(delta < 0){
28141             this.showNextMonth();
28142             e.stopEvent();
28143         }
28144     },
28145
28146     // private
28147     handleDateClick : function(e, t){
28148         e.stopEvent();
28149         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28150             this.setValue(new Date(t.dateValue));
28151             this.fireEvent("select", this, this.value);
28152         }
28153     },
28154
28155     // private
28156     selectToday : function(){
28157         this.setValue(new Date().clearTime());
28158         this.fireEvent("select", this, this.value);
28159     },
28160
28161     // private
28162     update : function(date)
28163     {
28164         var vd = this.activeDate;
28165         this.activeDate = date;
28166         if(vd && this.el){
28167             var t = date.getTime();
28168             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28169                 this.cells.removeClass("x-date-selected");
28170                 this.cells.each(function(c){
28171                    if(c.dom.firstChild.dateValue == t){
28172                        c.addClass("x-date-selected");
28173                        setTimeout(function(){
28174                             try{c.dom.firstChild.focus();}catch(e){}
28175                        }, 50);
28176                        return false;
28177                    }
28178                 });
28179                 return;
28180             }
28181         }
28182         
28183         var days = date.getDaysInMonth();
28184         var firstOfMonth = date.getFirstDateOfMonth();
28185         var startingPos = firstOfMonth.getDay()-this.startDay;
28186
28187         if(startingPos <= this.startDay){
28188             startingPos += 7;
28189         }
28190
28191         var pm = date.add("mo", -1);
28192         var prevStart = pm.getDaysInMonth()-startingPos;
28193
28194         var cells = this.cells.elements;
28195         var textEls = this.textNodes;
28196         days += startingPos;
28197
28198         // convert everything to numbers so it's fast
28199         var day = 86400000;
28200         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28201         var today = new Date().clearTime().getTime();
28202         var sel = date.clearTime().getTime();
28203         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28204         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28205         var ddMatch = this.disabledDatesRE;
28206         var ddText = this.disabledDatesText;
28207         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28208         var ddaysText = this.disabledDaysText;
28209         var format = this.format;
28210
28211         var setCellClass = function(cal, cell){
28212             cell.title = "";
28213             var t = d.getTime();
28214             cell.firstChild.dateValue = t;
28215             if(t == today){
28216                 cell.className += " x-date-today";
28217                 cell.title = cal.todayText;
28218             }
28219             if(t == sel){
28220                 cell.className += " x-date-selected";
28221                 setTimeout(function(){
28222                     try{cell.firstChild.focus();}catch(e){}
28223                 }, 50);
28224             }
28225             // disabling
28226             if(t < min) {
28227                 cell.className = " x-date-disabled";
28228                 cell.title = cal.minText;
28229                 return;
28230             }
28231             if(t > max) {
28232                 cell.className = " x-date-disabled";
28233                 cell.title = cal.maxText;
28234                 return;
28235             }
28236             if(ddays){
28237                 if(ddays.indexOf(d.getDay()) != -1){
28238                     cell.title = ddaysText;
28239                     cell.className = " x-date-disabled";
28240                 }
28241             }
28242             if(ddMatch && format){
28243                 var fvalue = d.dateFormat(format);
28244                 if(ddMatch.test(fvalue)){
28245                     cell.title = ddText.replace("%0", fvalue);
28246                     cell.className = " x-date-disabled";
28247                 }
28248             }
28249         };
28250
28251         var i = 0;
28252         for(; i < startingPos; i++) {
28253             textEls[i].innerHTML = (++prevStart);
28254             d.setDate(d.getDate()+1);
28255             cells[i].className = "x-date-prevday";
28256             setCellClass(this, cells[i]);
28257         }
28258         for(; i < days; i++){
28259             intDay = i - startingPos + 1;
28260             textEls[i].innerHTML = (intDay);
28261             d.setDate(d.getDate()+1);
28262             cells[i].className = "x-date-active";
28263             setCellClass(this, cells[i]);
28264         }
28265         var extraDays = 0;
28266         for(; i < 42; i++) {
28267              textEls[i].innerHTML = (++extraDays);
28268              d.setDate(d.getDate()+1);
28269              cells[i].className = "x-date-nextday";
28270              setCellClass(this, cells[i]);
28271         }
28272
28273         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28274         this.fireEvent('monthchange', this, date);
28275         
28276         if(!this.internalRender){
28277             var main = this.el.dom.firstChild;
28278             var w = main.offsetWidth;
28279             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28280             Roo.fly(main).setWidth(w);
28281             this.internalRender = true;
28282             // opera does not respect the auto grow header center column
28283             // then, after it gets a width opera refuses to recalculate
28284             // without a second pass
28285             if(Roo.isOpera && !this.secondPass){
28286                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28287                 this.secondPass = true;
28288                 this.update.defer(10, this, [date]);
28289             }
28290         }
28291         
28292         
28293     }
28294 });        /*
28295  * Based on:
28296  * Ext JS Library 1.1.1
28297  * Copyright(c) 2006-2007, Ext JS, LLC.
28298  *
28299  * Originally Released Under LGPL - original licence link has changed is not relivant.
28300  *
28301  * Fork - LGPL
28302  * <script type="text/javascript">
28303  */
28304 /**
28305  * @class Roo.TabPanel
28306  * @extends Roo.util.Observable
28307  * A lightweight tab container.
28308  * <br><br>
28309  * Usage:
28310  * <pre><code>
28311 // basic tabs 1, built from existing content
28312 var tabs = new Roo.TabPanel("tabs1");
28313 tabs.addTab("script", "View Script");
28314 tabs.addTab("markup", "View Markup");
28315 tabs.activate("script");
28316
28317 // more advanced tabs, built from javascript
28318 var jtabs = new Roo.TabPanel("jtabs");
28319 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28320
28321 // set up the UpdateManager
28322 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28323 var updater = tab2.getUpdateManager();
28324 updater.setDefaultUrl("ajax1.htm");
28325 tab2.on('activate', updater.refresh, updater, true);
28326
28327 // Use setUrl for Ajax loading
28328 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28329 tab3.setUrl("ajax2.htm", null, true);
28330
28331 // Disabled tab
28332 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28333 tab4.disable();
28334
28335 jtabs.activate("jtabs-1");
28336  * </code></pre>
28337  * @constructor
28338  * Create a new TabPanel.
28339  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28340  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28341  */
28342 Roo.TabPanel = function(container, config){
28343     /**
28344     * The container element for this TabPanel.
28345     * @type Roo.Element
28346     */
28347     this.el = Roo.get(container, true);
28348     if(config){
28349         if(typeof config == "boolean"){
28350             this.tabPosition = config ? "bottom" : "top";
28351         }else{
28352             Roo.apply(this, config);
28353         }
28354     }
28355     if(this.tabPosition == "bottom"){
28356         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28357         this.el.addClass("x-tabs-bottom");
28358     }
28359     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28360     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28361     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28362     if(Roo.isIE){
28363         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28364     }
28365     if(this.tabPosition != "bottom"){
28366         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28367          * @type Roo.Element
28368          */
28369         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28370         this.el.addClass("x-tabs-top");
28371     }
28372     this.items = [];
28373
28374     this.bodyEl.setStyle("position", "relative");
28375
28376     this.active = null;
28377     this.activateDelegate = this.activate.createDelegate(this);
28378
28379     this.addEvents({
28380         /**
28381          * @event tabchange
28382          * Fires when the active tab changes
28383          * @param {Roo.TabPanel} this
28384          * @param {Roo.TabPanelItem} activePanel The new active tab
28385          */
28386         "tabchange": true,
28387         /**
28388          * @event beforetabchange
28389          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28390          * @param {Roo.TabPanel} this
28391          * @param {Object} e Set cancel to true on this object to cancel the tab change
28392          * @param {Roo.TabPanelItem} tab The tab being changed to
28393          */
28394         "beforetabchange" : true
28395     });
28396
28397     Roo.EventManager.onWindowResize(this.onResize, this);
28398     this.cpad = this.el.getPadding("lr");
28399     this.hiddenCount = 0;
28400
28401
28402     // toolbar on the tabbar support...
28403     if (this.toolbar) {
28404         var tcfg = this.toolbar;
28405         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28406         this.toolbar = new Roo.Toolbar(tcfg);
28407         if (Roo.isSafari) {
28408             var tbl = tcfg.container.child('table', true);
28409             tbl.setAttribute('width', '100%');
28410         }
28411         
28412     }
28413    
28414
28415
28416     Roo.TabPanel.superclass.constructor.call(this);
28417 };
28418
28419 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28420     /*
28421      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28422      */
28423     tabPosition : "top",
28424     /*
28425      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28426      */
28427     currentTabWidth : 0,
28428     /*
28429      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28430      */
28431     minTabWidth : 40,
28432     /*
28433      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28434      */
28435     maxTabWidth : 250,
28436     /*
28437      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28438      */
28439     preferredTabWidth : 175,
28440     /*
28441      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28442      */
28443     resizeTabs : false,
28444     /*
28445      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28446      */
28447     monitorResize : true,
28448     /*
28449      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28450      */
28451     toolbar : false,
28452
28453     /**
28454      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28455      * @param {String} id The id of the div to use <b>or create</b>
28456      * @param {String} text The text for the tab
28457      * @param {String} content (optional) Content to put in the TabPanelItem body
28458      * @param {Boolean} closable (optional) True to create a close icon on the tab
28459      * @return {Roo.TabPanelItem} The created TabPanelItem
28460      */
28461     addTab : function(id, text, content, closable){
28462         var item = new Roo.TabPanelItem(this, id, text, closable);
28463         this.addTabItem(item);
28464         if(content){
28465             item.setContent(content);
28466         }
28467         return item;
28468     },
28469
28470     /**
28471      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28472      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28473      * @return {Roo.TabPanelItem}
28474      */
28475     getTab : function(id){
28476         return this.items[id];
28477     },
28478
28479     /**
28480      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28481      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28482      */
28483     hideTab : function(id){
28484         var t = this.items[id];
28485         if(!t.isHidden()){
28486            t.setHidden(true);
28487            this.hiddenCount++;
28488            this.autoSizeTabs();
28489         }
28490     },
28491
28492     /**
28493      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28494      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28495      */
28496     unhideTab : function(id){
28497         var t = this.items[id];
28498         if(t.isHidden()){
28499            t.setHidden(false);
28500            this.hiddenCount--;
28501            this.autoSizeTabs();
28502         }
28503     },
28504
28505     /**
28506      * Adds an existing {@link Roo.TabPanelItem}.
28507      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28508      */
28509     addTabItem : function(item){
28510         this.items[item.id] = item;
28511         this.items.push(item);
28512         if(this.resizeTabs){
28513            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28514            this.autoSizeTabs();
28515         }else{
28516             item.autoSize();
28517         }
28518     },
28519
28520     /**
28521      * Removes a {@link Roo.TabPanelItem}.
28522      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28523      */
28524     removeTab : function(id){
28525         var items = this.items;
28526         var tab = items[id];
28527         if(!tab) { return; }
28528         var index = items.indexOf(tab);
28529         if(this.active == tab && items.length > 1){
28530             var newTab = this.getNextAvailable(index);
28531             if(newTab) {
28532                 newTab.activate();
28533             }
28534         }
28535         this.stripEl.dom.removeChild(tab.pnode.dom);
28536         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28537             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28538         }
28539         items.splice(index, 1);
28540         delete this.items[tab.id];
28541         tab.fireEvent("close", tab);
28542         tab.purgeListeners();
28543         this.autoSizeTabs();
28544     },
28545
28546     getNextAvailable : function(start){
28547         var items = this.items;
28548         var index = start;
28549         // look for a next tab that will slide over to
28550         // replace the one being removed
28551         while(index < items.length){
28552             var item = items[++index];
28553             if(item && !item.isHidden()){
28554                 return item;
28555             }
28556         }
28557         // if one isn't found select the previous tab (on the left)
28558         index = start;
28559         while(index >= 0){
28560             var item = items[--index];
28561             if(item && !item.isHidden()){
28562                 return item;
28563             }
28564         }
28565         return null;
28566     },
28567
28568     /**
28569      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28570      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28571      */
28572     disableTab : function(id){
28573         var tab = this.items[id];
28574         if(tab && this.active != tab){
28575             tab.disable();
28576         }
28577     },
28578
28579     /**
28580      * Enables a {@link Roo.TabPanelItem} that is disabled.
28581      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28582      */
28583     enableTab : function(id){
28584         var tab = this.items[id];
28585         tab.enable();
28586     },
28587
28588     /**
28589      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28590      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28591      * @return {Roo.TabPanelItem} The TabPanelItem.
28592      */
28593     activate : function(id){
28594         var tab = this.items[id];
28595         if(!tab){
28596             return null;
28597         }
28598         if(tab == this.active || tab.disabled){
28599             return tab;
28600         }
28601         var e = {};
28602         this.fireEvent("beforetabchange", this, e, tab);
28603         if(e.cancel !== true && !tab.disabled){
28604             if(this.active){
28605                 this.active.hide();
28606             }
28607             this.active = this.items[id];
28608             this.active.show();
28609             this.fireEvent("tabchange", this, this.active);
28610         }
28611         return tab;
28612     },
28613
28614     /**
28615      * Gets the active {@link Roo.TabPanelItem}.
28616      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28617      */
28618     getActiveTab : function(){
28619         return this.active;
28620     },
28621
28622     /**
28623      * Updates the tab body element to fit the height of the container element
28624      * for overflow scrolling
28625      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28626      */
28627     syncHeight : function(targetHeight){
28628         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28629         var bm = this.bodyEl.getMargins();
28630         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28631         this.bodyEl.setHeight(newHeight);
28632         return newHeight;
28633     },
28634
28635     onResize : function(){
28636         if(this.monitorResize){
28637             this.autoSizeTabs();
28638         }
28639     },
28640
28641     /**
28642      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28643      */
28644     beginUpdate : function(){
28645         this.updating = true;
28646     },
28647
28648     /**
28649      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28650      */
28651     endUpdate : function(){
28652         this.updating = false;
28653         this.autoSizeTabs();
28654     },
28655
28656     /**
28657      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28658      */
28659     autoSizeTabs : function(){
28660         var count = this.items.length;
28661         var vcount = count - this.hiddenCount;
28662         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28663             return;
28664         }
28665         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28666         var availWidth = Math.floor(w / vcount);
28667         var b = this.stripBody;
28668         if(b.getWidth() > w){
28669             var tabs = this.items;
28670             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28671             if(availWidth < this.minTabWidth){
28672                 /*if(!this.sleft){    // incomplete scrolling code
28673                     this.createScrollButtons();
28674                 }
28675                 this.showScroll();
28676                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28677             }
28678         }else{
28679             if(this.currentTabWidth < this.preferredTabWidth){
28680                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28681             }
28682         }
28683     },
28684
28685     /**
28686      * Returns the number of tabs in this TabPanel.
28687      * @return {Number}
28688      */
28689      getCount : function(){
28690          return this.items.length;
28691      },
28692
28693     /**
28694      * Resizes all the tabs to the passed width
28695      * @param {Number} The new width
28696      */
28697     setTabWidth : function(width){
28698         this.currentTabWidth = width;
28699         for(var i = 0, len = this.items.length; i < len; i++) {
28700                 if(!this.items[i].isHidden()) {
28701                 this.items[i].setWidth(width);
28702             }
28703         }
28704     },
28705
28706     /**
28707      * Destroys this TabPanel
28708      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28709      */
28710     destroy : function(removeEl){
28711         Roo.EventManager.removeResizeListener(this.onResize, this);
28712         for(var i = 0, len = this.items.length; i < len; i++){
28713             this.items[i].purgeListeners();
28714         }
28715         if(removeEl === true){
28716             this.el.update("");
28717             this.el.remove();
28718         }
28719     }
28720 });
28721
28722 /**
28723  * @class Roo.TabPanelItem
28724  * @extends Roo.util.Observable
28725  * Represents an individual item (tab plus body) in a TabPanel.
28726  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28727  * @param {String} id The id of this TabPanelItem
28728  * @param {String} text The text for the tab of this TabPanelItem
28729  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28730  */
28731 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28732     /**
28733      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28734      * @type Roo.TabPanel
28735      */
28736     this.tabPanel = tabPanel;
28737     /**
28738      * The id for this TabPanelItem
28739      * @type String
28740      */
28741     this.id = id;
28742     /** @private */
28743     this.disabled = false;
28744     /** @private */
28745     this.text = text;
28746     /** @private */
28747     this.loaded = false;
28748     this.closable = closable;
28749
28750     /**
28751      * The body element for this TabPanelItem.
28752      * @type Roo.Element
28753      */
28754     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28755     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28756     this.bodyEl.setStyle("display", "block");
28757     this.bodyEl.setStyle("zoom", "1");
28758     this.hideAction();
28759
28760     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28761     /** @private */
28762     this.el = Roo.get(els.el, true);
28763     this.inner = Roo.get(els.inner, true);
28764     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28765     this.pnode = Roo.get(els.el.parentNode, true);
28766     this.el.on("mousedown", this.onTabMouseDown, this);
28767     this.el.on("click", this.onTabClick, this);
28768     /** @private */
28769     if(closable){
28770         var c = Roo.get(els.close, true);
28771         c.dom.title = this.closeText;
28772         c.addClassOnOver("close-over");
28773         c.on("click", this.closeClick, this);
28774      }
28775
28776     this.addEvents({
28777          /**
28778          * @event activate
28779          * Fires when this tab becomes the active tab.
28780          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28781          * @param {Roo.TabPanelItem} this
28782          */
28783         "activate": true,
28784         /**
28785          * @event beforeclose
28786          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28787          * @param {Roo.TabPanelItem} this
28788          * @param {Object} e Set cancel to true on this object to cancel the close.
28789          */
28790         "beforeclose": true,
28791         /**
28792          * @event close
28793          * Fires when this tab is closed.
28794          * @param {Roo.TabPanelItem} this
28795          */
28796          "close": true,
28797         /**
28798          * @event deactivate
28799          * Fires when this tab is no longer the active tab.
28800          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28801          * @param {Roo.TabPanelItem} this
28802          */
28803          "deactivate" : true
28804     });
28805     this.hidden = false;
28806
28807     Roo.TabPanelItem.superclass.constructor.call(this);
28808 };
28809
28810 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28811     purgeListeners : function(){
28812        Roo.util.Observable.prototype.purgeListeners.call(this);
28813        this.el.removeAllListeners();
28814     },
28815     /**
28816      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28817      */
28818     show : function(){
28819         this.pnode.addClass("on");
28820         this.showAction();
28821         if(Roo.isOpera){
28822             this.tabPanel.stripWrap.repaint();
28823         }
28824         this.fireEvent("activate", this.tabPanel, this);
28825     },
28826
28827     /**
28828      * Returns true if this tab is the active tab.
28829      * @return {Boolean}
28830      */
28831     isActive : function(){
28832         return this.tabPanel.getActiveTab() == this;
28833     },
28834
28835     /**
28836      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28837      */
28838     hide : function(){
28839         this.pnode.removeClass("on");
28840         this.hideAction();
28841         this.fireEvent("deactivate", this.tabPanel, this);
28842     },
28843
28844     hideAction : function(){
28845         this.bodyEl.hide();
28846         this.bodyEl.setStyle("position", "absolute");
28847         this.bodyEl.setLeft("-20000px");
28848         this.bodyEl.setTop("-20000px");
28849     },
28850
28851     showAction : function(){
28852         this.bodyEl.setStyle("position", "relative");
28853         this.bodyEl.setTop("");
28854         this.bodyEl.setLeft("");
28855         this.bodyEl.show();
28856     },
28857
28858     /**
28859      * Set the tooltip for the tab.
28860      * @param {String} tooltip The tab's tooltip
28861      */
28862     setTooltip : function(text){
28863         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28864             this.textEl.dom.qtip = text;
28865             this.textEl.dom.removeAttribute('title');
28866         }else{
28867             this.textEl.dom.title = text;
28868         }
28869     },
28870
28871     onTabClick : function(e){
28872         e.preventDefault();
28873         this.tabPanel.activate(this.id);
28874     },
28875
28876     onTabMouseDown : function(e){
28877         e.preventDefault();
28878         this.tabPanel.activate(this.id);
28879     },
28880
28881     getWidth : function(){
28882         return this.inner.getWidth();
28883     },
28884
28885     setWidth : function(width){
28886         var iwidth = width - this.pnode.getPadding("lr");
28887         this.inner.setWidth(iwidth);
28888         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28889         this.pnode.setWidth(width);
28890     },
28891
28892     /**
28893      * Show or hide the tab
28894      * @param {Boolean} hidden True to hide or false to show.
28895      */
28896     setHidden : function(hidden){
28897         this.hidden = hidden;
28898         this.pnode.setStyle("display", hidden ? "none" : "");
28899     },
28900
28901     /**
28902      * Returns true if this tab is "hidden"
28903      * @return {Boolean}
28904      */
28905     isHidden : function(){
28906         return this.hidden;
28907     },
28908
28909     /**
28910      * Returns the text for this tab
28911      * @return {String}
28912      */
28913     getText : function(){
28914         return this.text;
28915     },
28916
28917     autoSize : function(){
28918         //this.el.beginMeasure();
28919         this.textEl.setWidth(1);
28920         /*
28921          *  #2804 [new] Tabs in Roojs
28922          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28923          */
28924         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28925         //this.el.endMeasure();
28926     },
28927
28928     /**
28929      * Sets the text for the tab (Note: this also sets the tooltip text)
28930      * @param {String} text The tab's text and tooltip
28931      */
28932     setText : function(text){
28933         this.text = text;
28934         this.textEl.update(text);
28935         this.setTooltip(text);
28936         if(!this.tabPanel.resizeTabs){
28937             this.autoSize();
28938         }
28939     },
28940     /**
28941      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28942      */
28943     activate : function(){
28944         this.tabPanel.activate(this.id);
28945     },
28946
28947     /**
28948      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28949      */
28950     disable : function(){
28951         if(this.tabPanel.active != this){
28952             this.disabled = true;
28953             this.pnode.addClass("disabled");
28954         }
28955     },
28956
28957     /**
28958      * Enables this TabPanelItem if it was previously disabled.
28959      */
28960     enable : function(){
28961         this.disabled = false;
28962         this.pnode.removeClass("disabled");
28963     },
28964
28965     /**
28966      * Sets the content for this TabPanelItem.
28967      * @param {String} content The content
28968      * @param {Boolean} loadScripts true to look for and load scripts
28969      */
28970     setContent : function(content, loadScripts){
28971         this.bodyEl.update(content, loadScripts);
28972     },
28973
28974     /**
28975      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28976      * @return {Roo.UpdateManager} The UpdateManager
28977      */
28978     getUpdateManager : function(){
28979         return this.bodyEl.getUpdateManager();
28980     },
28981
28982     /**
28983      * Set a URL to be used to load the content for this TabPanelItem.
28984      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
28985      * @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)
28986      * @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)
28987      * @return {Roo.UpdateManager} The UpdateManager
28988      */
28989     setUrl : function(url, params, loadOnce){
28990         if(this.refreshDelegate){
28991             this.un('activate', this.refreshDelegate);
28992         }
28993         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
28994         this.on("activate", this.refreshDelegate);
28995         return this.bodyEl.getUpdateManager();
28996     },
28997
28998     /** @private */
28999     _handleRefresh : function(url, params, loadOnce){
29000         if(!loadOnce || !this.loaded){
29001             var updater = this.bodyEl.getUpdateManager();
29002             updater.update(url, params, this._setLoaded.createDelegate(this));
29003         }
29004     },
29005
29006     /**
29007      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29008      *   Will fail silently if the setUrl method has not been called.
29009      *   This does not activate the panel, just updates its content.
29010      */
29011     refresh : function(){
29012         if(this.refreshDelegate){
29013            this.loaded = false;
29014            this.refreshDelegate();
29015         }
29016     },
29017
29018     /** @private */
29019     _setLoaded : function(){
29020         this.loaded = true;
29021     },
29022
29023     /** @private */
29024     closeClick : function(e){
29025         var o = {};
29026         e.stopEvent();
29027         this.fireEvent("beforeclose", this, o);
29028         if(o.cancel !== true){
29029             this.tabPanel.removeTab(this.id);
29030         }
29031     },
29032     /**
29033      * The text displayed in the tooltip for the close icon.
29034      * @type String
29035      */
29036     closeText : "Close this tab"
29037 });
29038
29039 /** @private */
29040 Roo.TabPanel.prototype.createStrip = function(container){
29041     var strip = document.createElement("div");
29042     strip.className = "x-tabs-wrap";
29043     container.appendChild(strip);
29044     return strip;
29045 };
29046 /** @private */
29047 Roo.TabPanel.prototype.createStripList = function(strip){
29048     // div wrapper for retard IE
29049     // returns the "tr" element.
29050     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29051         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29052         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29053     return strip.firstChild.firstChild.firstChild.firstChild;
29054 };
29055 /** @private */
29056 Roo.TabPanel.prototype.createBody = function(container){
29057     var body = document.createElement("div");
29058     Roo.id(body, "tab-body");
29059     Roo.fly(body).addClass("x-tabs-body");
29060     container.appendChild(body);
29061     return body;
29062 };
29063 /** @private */
29064 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29065     var body = Roo.getDom(id);
29066     if(!body){
29067         body = document.createElement("div");
29068         body.id = id;
29069     }
29070     Roo.fly(body).addClass("x-tabs-item-body");
29071     bodyEl.insertBefore(body, bodyEl.firstChild);
29072     return body;
29073 };
29074 /** @private */
29075 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29076     var td = document.createElement("td");
29077     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29078     //stripEl.appendChild(td);
29079     if(closable){
29080         td.className = "x-tabs-closable";
29081         if(!this.closeTpl){
29082             this.closeTpl = new Roo.Template(
29083                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29084                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29085                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29086             );
29087         }
29088         var el = this.closeTpl.overwrite(td, {"text": text});
29089         var close = el.getElementsByTagName("div")[0];
29090         var inner = el.getElementsByTagName("em")[0];
29091         return {"el": el, "close": close, "inner": inner};
29092     } else {
29093         if(!this.tabTpl){
29094             this.tabTpl = new Roo.Template(
29095                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29096                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29097             );
29098         }
29099         var el = this.tabTpl.overwrite(td, {"text": text});
29100         var inner = el.getElementsByTagName("em")[0];
29101         return {"el": el, "inner": inner};
29102     }
29103 };/*
29104  * Based on:
29105  * Ext JS Library 1.1.1
29106  * Copyright(c) 2006-2007, Ext JS, LLC.
29107  *
29108  * Originally Released Under LGPL - original licence link has changed is not relivant.
29109  *
29110  * Fork - LGPL
29111  * <script type="text/javascript">
29112  */
29113
29114 /**
29115  * @class Roo.Button
29116  * @extends Roo.util.Observable
29117  * Simple Button class
29118  * @cfg {String} text The button text
29119  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29120  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29121  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29122  * @cfg {Object} scope The scope of the handler
29123  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29124  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29125  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29126  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29127  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29128  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29129    applies if enableToggle = true)
29130  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29131  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29132   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29133  * @constructor
29134  * Create a new button
29135  * @param {Object} config The config object
29136  */
29137 Roo.Button = function(renderTo, config)
29138 {
29139     if (!config) {
29140         config = renderTo;
29141         renderTo = config.renderTo || false;
29142     }
29143     
29144     Roo.apply(this, config);
29145     this.addEvents({
29146         /**
29147              * @event click
29148              * Fires when this button is clicked
29149              * @param {Button} this
29150              * @param {EventObject} e The click event
29151              */
29152             "click" : true,
29153         /**
29154              * @event toggle
29155              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29156              * @param {Button} this
29157              * @param {Boolean} pressed
29158              */
29159             "toggle" : true,
29160         /**
29161              * @event mouseover
29162              * Fires when the mouse hovers over the button
29163              * @param {Button} this
29164              * @param {Event} e The event object
29165              */
29166         'mouseover' : true,
29167         /**
29168              * @event mouseout
29169              * Fires when the mouse exits the button
29170              * @param {Button} this
29171              * @param {Event} e The event object
29172              */
29173         'mouseout': true,
29174          /**
29175              * @event render
29176              * Fires when the button is rendered
29177              * @param {Button} this
29178              */
29179         'render': true
29180     });
29181     if(this.menu){
29182         this.menu = Roo.menu.MenuMgr.get(this.menu);
29183     }
29184     // register listeners first!!  - so render can be captured..
29185     Roo.util.Observable.call(this);
29186     if(renderTo){
29187         this.render(renderTo);
29188     }
29189     
29190   
29191 };
29192
29193 Roo.extend(Roo.Button, Roo.util.Observable, {
29194     /**
29195      * 
29196      */
29197     
29198     /**
29199      * Read-only. True if this button is hidden
29200      * @type Boolean
29201      */
29202     hidden : false,
29203     /**
29204      * Read-only. True if this button is disabled
29205      * @type Boolean
29206      */
29207     disabled : false,
29208     /**
29209      * Read-only. True if this button is pressed (only if enableToggle = true)
29210      * @type Boolean
29211      */
29212     pressed : false,
29213
29214     /**
29215      * @cfg {Number} tabIndex 
29216      * The DOM tabIndex for this button (defaults to undefined)
29217      */
29218     tabIndex : undefined,
29219
29220     /**
29221      * @cfg {Boolean} enableToggle
29222      * True to enable pressed/not pressed toggling (defaults to false)
29223      */
29224     enableToggle: false,
29225     /**
29226      * @cfg {Mixed} menu
29227      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29228      */
29229     menu : undefined,
29230     /**
29231      * @cfg {String} menuAlign
29232      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29233      */
29234     menuAlign : "tl-bl?",
29235
29236     /**
29237      * @cfg {String} iconCls
29238      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29239      */
29240     iconCls : undefined,
29241     /**
29242      * @cfg {String} type
29243      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29244      */
29245     type : 'button',
29246
29247     // private
29248     menuClassTarget: 'tr',
29249
29250     /**
29251      * @cfg {String} clickEvent
29252      * The type of event to map to the button's event handler (defaults to 'click')
29253      */
29254     clickEvent : 'click',
29255
29256     /**
29257      * @cfg {Boolean} handleMouseEvents
29258      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29259      */
29260     handleMouseEvents : true,
29261
29262     /**
29263      * @cfg {String} tooltipType
29264      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29265      */
29266     tooltipType : 'qtip',
29267
29268     /**
29269      * @cfg {String} cls
29270      * A CSS class to apply to the button's main element.
29271      */
29272     
29273     /**
29274      * @cfg {Roo.Template} template (Optional)
29275      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29276      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29277      * require code modifications if required elements (e.g. a button) aren't present.
29278      */
29279
29280     // private
29281     render : function(renderTo){
29282         var btn;
29283         if(this.hideParent){
29284             this.parentEl = Roo.get(renderTo);
29285         }
29286         if(!this.dhconfig){
29287             if(!this.template){
29288                 if(!Roo.Button.buttonTemplate){
29289                     // hideous table template
29290                     Roo.Button.buttonTemplate = new Roo.Template(
29291                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29292                         '<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>',
29293                         "</tr></tbody></table>");
29294                 }
29295                 this.template = Roo.Button.buttonTemplate;
29296             }
29297             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29298             var btnEl = btn.child("button:first");
29299             btnEl.on('focus', this.onFocus, this);
29300             btnEl.on('blur', this.onBlur, this);
29301             if(this.cls){
29302                 btn.addClass(this.cls);
29303             }
29304             if(this.icon){
29305                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29306             }
29307             if(this.iconCls){
29308                 btnEl.addClass(this.iconCls);
29309                 if(!this.cls){
29310                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29311                 }
29312             }
29313             if(this.tabIndex !== undefined){
29314                 btnEl.dom.tabIndex = this.tabIndex;
29315             }
29316             if(this.tooltip){
29317                 if(typeof this.tooltip == 'object'){
29318                     Roo.QuickTips.tips(Roo.apply({
29319                           target: btnEl.id
29320                     }, this.tooltip));
29321                 } else {
29322                     btnEl.dom[this.tooltipType] = this.tooltip;
29323                 }
29324             }
29325         }else{
29326             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29327         }
29328         this.el = btn;
29329         if(this.id){
29330             this.el.dom.id = this.el.id = this.id;
29331         }
29332         if(this.menu){
29333             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29334             this.menu.on("show", this.onMenuShow, this);
29335             this.menu.on("hide", this.onMenuHide, this);
29336         }
29337         btn.addClass("x-btn");
29338         if(Roo.isIE && !Roo.isIE7){
29339             this.autoWidth.defer(1, this);
29340         }else{
29341             this.autoWidth();
29342         }
29343         if(this.handleMouseEvents){
29344             btn.on("mouseover", this.onMouseOver, this);
29345             btn.on("mouseout", this.onMouseOut, this);
29346             btn.on("mousedown", this.onMouseDown, this);
29347         }
29348         btn.on(this.clickEvent, this.onClick, this);
29349         //btn.on("mouseup", this.onMouseUp, this);
29350         if(this.hidden){
29351             this.hide();
29352         }
29353         if(this.disabled){
29354             this.disable();
29355         }
29356         Roo.ButtonToggleMgr.register(this);
29357         if(this.pressed){
29358             this.el.addClass("x-btn-pressed");
29359         }
29360         if(this.repeat){
29361             var repeater = new Roo.util.ClickRepeater(btn,
29362                 typeof this.repeat == "object" ? this.repeat : {}
29363             );
29364             repeater.on("click", this.onClick,  this);
29365         }
29366         
29367         this.fireEvent('render', this);
29368         
29369     },
29370     /**
29371      * Returns the button's underlying element
29372      * @return {Roo.Element} The element
29373      */
29374     getEl : function(){
29375         return this.el;  
29376     },
29377     
29378     /**
29379      * Destroys this Button and removes any listeners.
29380      */
29381     destroy : function(){
29382         Roo.ButtonToggleMgr.unregister(this);
29383         this.el.removeAllListeners();
29384         this.purgeListeners();
29385         this.el.remove();
29386     },
29387
29388     // private
29389     autoWidth : function(){
29390         if(this.el){
29391             this.el.setWidth("auto");
29392             if(Roo.isIE7 && Roo.isStrict){
29393                 var ib = this.el.child('button');
29394                 if(ib && ib.getWidth() > 20){
29395                     ib.clip();
29396                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29397                 }
29398             }
29399             if(this.minWidth){
29400                 if(this.hidden){
29401                     this.el.beginMeasure();
29402                 }
29403                 if(this.el.getWidth() < this.minWidth){
29404                     this.el.setWidth(this.minWidth);
29405                 }
29406                 if(this.hidden){
29407                     this.el.endMeasure();
29408                 }
29409             }
29410         }
29411     },
29412
29413     /**
29414      * Assigns this button's click handler
29415      * @param {Function} handler The function to call when the button is clicked
29416      * @param {Object} scope (optional) Scope for the function passed in
29417      */
29418     setHandler : function(handler, scope){
29419         this.handler = handler;
29420         this.scope = scope;  
29421     },
29422     
29423     /**
29424      * Sets this button's text
29425      * @param {String} text The button text
29426      */
29427     setText : function(text){
29428         this.text = text;
29429         if(this.el){
29430             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29431         }
29432         this.autoWidth();
29433     },
29434     
29435     /**
29436      * Gets the text for this button
29437      * @return {String} The button text
29438      */
29439     getText : function(){
29440         return this.text;  
29441     },
29442     
29443     /**
29444      * Show this button
29445      */
29446     show: function(){
29447         this.hidden = false;
29448         if(this.el){
29449             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29450         }
29451     },
29452     
29453     /**
29454      * Hide this button
29455      */
29456     hide: function(){
29457         this.hidden = true;
29458         if(this.el){
29459             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29460         }
29461     },
29462     
29463     /**
29464      * Convenience function for boolean show/hide
29465      * @param {Boolean} visible True to show, false to hide
29466      */
29467     setVisible: function(visible){
29468         if(visible) {
29469             this.show();
29470         }else{
29471             this.hide();
29472         }
29473     },
29474     
29475     /**
29476      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29477      * @param {Boolean} state (optional) Force a particular state
29478      */
29479     toggle : function(state){
29480         state = state === undefined ? !this.pressed : state;
29481         if(state != this.pressed){
29482             if(state){
29483                 this.el.addClass("x-btn-pressed");
29484                 this.pressed = true;
29485                 this.fireEvent("toggle", this, true);
29486             }else{
29487                 this.el.removeClass("x-btn-pressed");
29488                 this.pressed = false;
29489                 this.fireEvent("toggle", this, false);
29490             }
29491             if(this.toggleHandler){
29492                 this.toggleHandler.call(this.scope || this, this, state);
29493             }
29494         }
29495     },
29496     
29497     /**
29498      * Focus the button
29499      */
29500     focus : function(){
29501         this.el.child('button:first').focus();
29502     },
29503     
29504     /**
29505      * Disable this button
29506      */
29507     disable : function(){
29508         if(this.el){
29509             this.el.addClass("x-btn-disabled");
29510         }
29511         this.disabled = true;
29512     },
29513     
29514     /**
29515      * Enable this button
29516      */
29517     enable : function(){
29518         if(this.el){
29519             this.el.removeClass("x-btn-disabled");
29520         }
29521         this.disabled = false;
29522     },
29523
29524     /**
29525      * Convenience function for boolean enable/disable
29526      * @param {Boolean} enabled True to enable, false to disable
29527      */
29528     setDisabled : function(v){
29529         this[v !== true ? "enable" : "disable"]();
29530     },
29531
29532     // private
29533     onClick : function(e)
29534     {
29535         if(e){
29536             e.preventDefault();
29537         }
29538         if(e.button != 0){
29539             return;
29540         }
29541         if(!this.disabled){
29542             if(this.enableToggle){
29543                 this.toggle();
29544             }
29545             if(this.menu && !this.menu.isVisible()){
29546                 this.menu.show(this.el, this.menuAlign);
29547             }
29548             this.fireEvent("click", this, e);
29549             if(this.handler){
29550                 this.el.removeClass("x-btn-over");
29551                 this.handler.call(this.scope || this, this, e);
29552             }
29553         }
29554     },
29555     // private
29556     onMouseOver : function(e){
29557         if(!this.disabled){
29558             this.el.addClass("x-btn-over");
29559             this.fireEvent('mouseover', this, e);
29560         }
29561     },
29562     // private
29563     onMouseOut : function(e){
29564         if(!e.within(this.el,  true)){
29565             this.el.removeClass("x-btn-over");
29566             this.fireEvent('mouseout', this, e);
29567         }
29568     },
29569     // private
29570     onFocus : function(e){
29571         if(!this.disabled){
29572             this.el.addClass("x-btn-focus");
29573         }
29574     },
29575     // private
29576     onBlur : function(e){
29577         this.el.removeClass("x-btn-focus");
29578     },
29579     // private
29580     onMouseDown : function(e){
29581         if(!this.disabled && e.button == 0){
29582             this.el.addClass("x-btn-click");
29583             Roo.get(document).on('mouseup', this.onMouseUp, this);
29584         }
29585     },
29586     // private
29587     onMouseUp : function(e){
29588         if(e.button == 0){
29589             this.el.removeClass("x-btn-click");
29590             Roo.get(document).un('mouseup', this.onMouseUp, this);
29591         }
29592     },
29593     // private
29594     onMenuShow : function(e){
29595         this.el.addClass("x-btn-menu-active");
29596     },
29597     // private
29598     onMenuHide : function(e){
29599         this.el.removeClass("x-btn-menu-active");
29600     }   
29601 });
29602
29603 // Private utility class used by Button
29604 Roo.ButtonToggleMgr = function(){
29605    var groups = {};
29606    
29607    function toggleGroup(btn, state){
29608        if(state){
29609            var g = groups[btn.toggleGroup];
29610            for(var i = 0, l = g.length; i < l; i++){
29611                if(g[i] != btn){
29612                    g[i].toggle(false);
29613                }
29614            }
29615        }
29616    }
29617    
29618    return {
29619        register : function(btn){
29620            if(!btn.toggleGroup){
29621                return;
29622            }
29623            var g = groups[btn.toggleGroup];
29624            if(!g){
29625                g = groups[btn.toggleGroup] = [];
29626            }
29627            g.push(btn);
29628            btn.on("toggle", toggleGroup);
29629        },
29630        
29631        unregister : function(btn){
29632            if(!btn.toggleGroup){
29633                return;
29634            }
29635            var g = groups[btn.toggleGroup];
29636            if(g){
29637                g.remove(btn);
29638                btn.un("toggle", toggleGroup);
29639            }
29640        }
29641    };
29642 }();/*
29643  * Based on:
29644  * Ext JS Library 1.1.1
29645  * Copyright(c) 2006-2007, Ext JS, LLC.
29646  *
29647  * Originally Released Under LGPL - original licence link has changed is not relivant.
29648  *
29649  * Fork - LGPL
29650  * <script type="text/javascript">
29651  */
29652  
29653 /**
29654  * @class Roo.SplitButton
29655  * @extends Roo.Button
29656  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29657  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29658  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29659  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29660  * @cfg {String} arrowTooltip The title attribute of the arrow
29661  * @constructor
29662  * Create a new menu button
29663  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29664  * @param {Object} config The config object
29665  */
29666 Roo.SplitButton = function(renderTo, config){
29667     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29668     /**
29669      * @event arrowclick
29670      * Fires when this button's arrow is clicked
29671      * @param {SplitButton} this
29672      * @param {EventObject} e The click event
29673      */
29674     this.addEvents({"arrowclick":true});
29675 };
29676
29677 Roo.extend(Roo.SplitButton, Roo.Button, {
29678     render : function(renderTo){
29679         // this is one sweet looking template!
29680         var tpl = new Roo.Template(
29681             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29682             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29683             '<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>',
29684             "</tbody></table></td><td>",
29685             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29686             '<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>',
29687             "</tbody></table></td></tr></table>"
29688         );
29689         var btn = tpl.append(renderTo, [this.text, this.type], true);
29690         var btnEl = btn.child("button");
29691         if(this.cls){
29692             btn.addClass(this.cls);
29693         }
29694         if(this.icon){
29695             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29696         }
29697         if(this.iconCls){
29698             btnEl.addClass(this.iconCls);
29699             if(!this.cls){
29700                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29701             }
29702         }
29703         this.el = btn;
29704         if(this.handleMouseEvents){
29705             btn.on("mouseover", this.onMouseOver, this);
29706             btn.on("mouseout", this.onMouseOut, this);
29707             btn.on("mousedown", this.onMouseDown, this);
29708             btn.on("mouseup", this.onMouseUp, this);
29709         }
29710         btn.on(this.clickEvent, this.onClick, this);
29711         if(this.tooltip){
29712             if(typeof this.tooltip == 'object'){
29713                 Roo.QuickTips.tips(Roo.apply({
29714                       target: btnEl.id
29715                 }, this.tooltip));
29716             } else {
29717                 btnEl.dom[this.tooltipType] = this.tooltip;
29718             }
29719         }
29720         if(this.arrowTooltip){
29721             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29722         }
29723         if(this.hidden){
29724             this.hide();
29725         }
29726         if(this.disabled){
29727             this.disable();
29728         }
29729         if(this.pressed){
29730             this.el.addClass("x-btn-pressed");
29731         }
29732         if(Roo.isIE && !Roo.isIE7){
29733             this.autoWidth.defer(1, this);
29734         }else{
29735             this.autoWidth();
29736         }
29737         if(this.menu){
29738             this.menu.on("show", this.onMenuShow, this);
29739             this.menu.on("hide", this.onMenuHide, this);
29740         }
29741         this.fireEvent('render', this);
29742     },
29743
29744     // private
29745     autoWidth : function(){
29746         if(this.el){
29747             var tbl = this.el.child("table:first");
29748             var tbl2 = this.el.child("table:last");
29749             this.el.setWidth("auto");
29750             tbl.setWidth("auto");
29751             if(Roo.isIE7 && Roo.isStrict){
29752                 var ib = this.el.child('button:first');
29753                 if(ib && ib.getWidth() > 20){
29754                     ib.clip();
29755                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29756                 }
29757             }
29758             if(this.minWidth){
29759                 if(this.hidden){
29760                     this.el.beginMeasure();
29761                 }
29762                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29763                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29764                 }
29765                 if(this.hidden){
29766                     this.el.endMeasure();
29767                 }
29768             }
29769             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29770         } 
29771     },
29772     /**
29773      * Sets this button's click handler
29774      * @param {Function} handler The function to call when the button is clicked
29775      * @param {Object} scope (optional) Scope for the function passed above
29776      */
29777     setHandler : function(handler, scope){
29778         this.handler = handler;
29779         this.scope = scope;  
29780     },
29781     
29782     /**
29783      * Sets this button's arrow click handler
29784      * @param {Function} handler The function to call when the arrow is clicked
29785      * @param {Object} scope (optional) Scope for the function passed above
29786      */
29787     setArrowHandler : function(handler, scope){
29788         this.arrowHandler = handler;
29789         this.scope = scope;  
29790     },
29791     
29792     /**
29793      * Focus the button
29794      */
29795     focus : function(){
29796         if(this.el){
29797             this.el.child("button:first").focus();
29798         }
29799     },
29800
29801     // private
29802     onClick : function(e){
29803         e.preventDefault();
29804         if(!this.disabled){
29805             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29806                 if(this.menu && !this.menu.isVisible()){
29807                     this.menu.show(this.el, this.menuAlign);
29808                 }
29809                 this.fireEvent("arrowclick", this, e);
29810                 if(this.arrowHandler){
29811                     this.arrowHandler.call(this.scope || this, this, e);
29812                 }
29813             }else{
29814                 this.fireEvent("click", this, e);
29815                 if(this.handler){
29816                     this.handler.call(this.scope || this, this, e);
29817                 }
29818             }
29819         }
29820     },
29821     // private
29822     onMouseDown : function(e){
29823         if(!this.disabled){
29824             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29825         }
29826     },
29827     // private
29828     onMouseUp : function(e){
29829         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29830     }   
29831 });
29832
29833
29834 // backwards compat
29835 Roo.MenuButton = Roo.SplitButton;/*
29836  * Based on:
29837  * Ext JS Library 1.1.1
29838  * Copyright(c) 2006-2007, Ext JS, LLC.
29839  *
29840  * Originally Released Under LGPL - original licence link has changed is not relivant.
29841  *
29842  * Fork - LGPL
29843  * <script type="text/javascript">
29844  */
29845
29846 /**
29847  * @class Roo.Toolbar
29848  * Basic Toolbar class.
29849  * @constructor
29850  * Creates a new Toolbar
29851  * @param {Object} container The config object
29852  */ 
29853 Roo.Toolbar = function(container, buttons, config)
29854 {
29855     /// old consturctor format still supported..
29856     if(container instanceof Array){ // omit the container for later rendering
29857         buttons = container;
29858         config = buttons;
29859         container = null;
29860     }
29861     if (typeof(container) == 'object' && container.xtype) {
29862         config = container;
29863         container = config.container;
29864         buttons = config.buttons || []; // not really - use items!!
29865     }
29866     var xitems = [];
29867     if (config && config.items) {
29868         xitems = config.items;
29869         delete config.items;
29870     }
29871     Roo.apply(this, config);
29872     this.buttons = buttons;
29873     
29874     if(container){
29875         this.render(container);
29876     }
29877     this.xitems = xitems;
29878     Roo.each(xitems, function(b) {
29879         this.add(b);
29880     }, this);
29881     
29882 };
29883
29884 Roo.Toolbar.prototype = {
29885     /**
29886      * @cfg {Array} items
29887      * array of button configs or elements to add (will be converted to a MixedCollection)
29888      */
29889     
29890     /**
29891      * @cfg {String/HTMLElement/Element} container
29892      * The id or element that will contain the toolbar
29893      */
29894     // private
29895     render : function(ct){
29896         this.el = Roo.get(ct);
29897         if(this.cls){
29898             this.el.addClass(this.cls);
29899         }
29900         // using a table allows for vertical alignment
29901         // 100% width is needed by Safari...
29902         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29903         this.tr = this.el.child("tr", true);
29904         var autoId = 0;
29905         this.items = new Roo.util.MixedCollection(false, function(o){
29906             return o.id || ("item" + (++autoId));
29907         });
29908         if(this.buttons){
29909             this.add.apply(this, this.buttons);
29910             delete this.buttons;
29911         }
29912     },
29913
29914     /**
29915      * Adds element(s) to the toolbar -- this function takes a variable number of 
29916      * arguments of mixed type and adds them to the toolbar.
29917      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29918      * <ul>
29919      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29920      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29921      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29922      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29923      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29924      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29925      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29926      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29927      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29928      * </ul>
29929      * @param {Mixed} arg2
29930      * @param {Mixed} etc.
29931      */
29932     add : function(){
29933         var a = arguments, l = a.length;
29934         for(var i = 0; i < l; i++){
29935             this._add(a[i]);
29936         }
29937     },
29938     // private..
29939     _add : function(el) {
29940         
29941         if (el.xtype) {
29942             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29943         }
29944         
29945         if (el.applyTo){ // some kind of form field
29946             return this.addField(el);
29947         } 
29948         if (el.render){ // some kind of Toolbar.Item
29949             return this.addItem(el);
29950         }
29951         if (typeof el == "string"){ // string
29952             if(el == "separator" || el == "-"){
29953                 return this.addSeparator();
29954             }
29955             if (el == " "){
29956                 return this.addSpacer();
29957             }
29958             if(el == "->"){
29959                 return this.addFill();
29960             }
29961             return this.addText(el);
29962             
29963         }
29964         if(el.tagName){ // element
29965             return this.addElement(el);
29966         }
29967         if(typeof el == "object"){ // must be button config?
29968             return this.addButton(el);
29969         }
29970         // and now what?!?!
29971         return false;
29972         
29973     },
29974     
29975     /**
29976      * Add an Xtype element
29977      * @param {Object} xtype Xtype Object
29978      * @return {Object} created Object
29979      */
29980     addxtype : function(e){
29981         return this.add(e);  
29982     },
29983     
29984     /**
29985      * Returns the Element for this toolbar.
29986      * @return {Roo.Element}
29987      */
29988     getEl : function(){
29989         return this.el;  
29990     },
29991     
29992     /**
29993      * Adds a separator
29994      * @return {Roo.Toolbar.Item} The separator item
29995      */
29996     addSeparator : function(){
29997         return this.addItem(new Roo.Toolbar.Separator());
29998     },
29999
30000     /**
30001      * Adds a spacer element
30002      * @return {Roo.Toolbar.Spacer} The spacer item
30003      */
30004     addSpacer : function(){
30005         return this.addItem(new Roo.Toolbar.Spacer());
30006     },
30007
30008     /**
30009      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30010      * @return {Roo.Toolbar.Fill} The fill item
30011      */
30012     addFill : function(){
30013         return this.addItem(new Roo.Toolbar.Fill());
30014     },
30015
30016     /**
30017      * Adds any standard HTML element to the toolbar
30018      * @param {String/HTMLElement/Element} el The element or id of the element to add
30019      * @return {Roo.Toolbar.Item} The element's item
30020      */
30021     addElement : function(el){
30022         return this.addItem(new Roo.Toolbar.Item(el));
30023     },
30024     /**
30025      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30026      * @type Roo.util.MixedCollection  
30027      */
30028     items : false,
30029      
30030     /**
30031      * Adds any Toolbar.Item or subclass
30032      * @param {Roo.Toolbar.Item} item
30033      * @return {Roo.Toolbar.Item} The item
30034      */
30035     addItem : function(item){
30036         var td = this.nextBlock();
30037         item.render(td);
30038         this.items.add(item);
30039         return item;
30040     },
30041     
30042     /**
30043      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30044      * @param {Object/Array} config A button config or array of configs
30045      * @return {Roo.Toolbar.Button/Array}
30046      */
30047     addButton : function(config){
30048         if(config instanceof Array){
30049             var buttons = [];
30050             for(var i = 0, len = config.length; i < len; i++) {
30051                 buttons.push(this.addButton(config[i]));
30052             }
30053             return buttons;
30054         }
30055         var b = config;
30056         if(!(config instanceof Roo.Toolbar.Button)){
30057             b = config.split ?
30058                 new Roo.Toolbar.SplitButton(config) :
30059                 new Roo.Toolbar.Button(config);
30060         }
30061         var td = this.nextBlock();
30062         b.render(td);
30063         this.items.add(b);
30064         return b;
30065     },
30066     
30067     /**
30068      * Adds text to the toolbar
30069      * @param {String} text The text to add
30070      * @return {Roo.Toolbar.Item} The element's item
30071      */
30072     addText : function(text){
30073         return this.addItem(new Roo.Toolbar.TextItem(text));
30074     },
30075     
30076     /**
30077      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30078      * @param {Number} index The index where the item is to be inserted
30079      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30080      * @return {Roo.Toolbar.Button/Item}
30081      */
30082     insertButton : function(index, item){
30083         if(item instanceof Array){
30084             var buttons = [];
30085             for(var i = 0, len = item.length; i < len; i++) {
30086                buttons.push(this.insertButton(index + i, item[i]));
30087             }
30088             return buttons;
30089         }
30090         if (!(item instanceof Roo.Toolbar.Button)){
30091            item = new Roo.Toolbar.Button(item);
30092         }
30093         var td = document.createElement("td");
30094         this.tr.insertBefore(td, this.tr.childNodes[index]);
30095         item.render(td);
30096         this.items.insert(index, item);
30097         return item;
30098     },
30099     
30100     /**
30101      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30102      * @param {Object} config
30103      * @return {Roo.Toolbar.Item} The element's item
30104      */
30105     addDom : function(config, returnEl){
30106         var td = this.nextBlock();
30107         Roo.DomHelper.overwrite(td, config);
30108         var ti = new Roo.Toolbar.Item(td.firstChild);
30109         ti.render(td);
30110         this.items.add(ti);
30111         return ti;
30112     },
30113
30114     /**
30115      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30116      * @type Roo.util.MixedCollection  
30117      */
30118     fields : false,
30119     
30120     /**
30121      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30122      * Note: the field should not have been rendered yet. For a field that has already been
30123      * rendered, use {@link #addElement}.
30124      * @param {Roo.form.Field} field
30125      * @return {Roo.ToolbarItem}
30126      */
30127      
30128       
30129     addField : function(field) {
30130         if (!this.fields) {
30131             var autoId = 0;
30132             this.fields = new Roo.util.MixedCollection(false, function(o){
30133                 return o.id || ("item" + (++autoId));
30134             });
30135
30136         }
30137         
30138         var td = this.nextBlock();
30139         field.render(td);
30140         var ti = new Roo.Toolbar.Item(td.firstChild);
30141         ti.render(td);
30142         this.items.add(ti);
30143         this.fields.add(field);
30144         return ti;
30145     },
30146     /**
30147      * Hide the toolbar
30148      * @method hide
30149      */
30150      
30151       
30152     hide : function()
30153     {
30154         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30155         this.el.child('div').hide();
30156     },
30157     /**
30158      * Show the toolbar
30159      * @method show
30160      */
30161     show : function()
30162     {
30163         this.el.child('div').show();
30164     },
30165       
30166     // private
30167     nextBlock : function(){
30168         var td = document.createElement("td");
30169         this.tr.appendChild(td);
30170         return td;
30171     },
30172
30173     // private
30174     destroy : function(){
30175         if(this.items){ // rendered?
30176             Roo.destroy.apply(Roo, this.items.items);
30177         }
30178         if(this.fields){ // rendered?
30179             Roo.destroy.apply(Roo, this.fields.items);
30180         }
30181         Roo.Element.uncache(this.el, this.tr);
30182     }
30183 };
30184
30185 /**
30186  * @class Roo.Toolbar.Item
30187  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30188  * @constructor
30189  * Creates a new Item
30190  * @param {HTMLElement} el 
30191  */
30192 Roo.Toolbar.Item = function(el){
30193     var cfg = {};
30194     if (typeof (el.xtype) != 'undefined') {
30195         cfg = el;
30196         el = cfg.el;
30197     }
30198     
30199     this.el = Roo.getDom(el);
30200     this.id = Roo.id(this.el);
30201     this.hidden = false;
30202     
30203     this.addEvents({
30204          /**
30205              * @event render
30206              * Fires when the button is rendered
30207              * @param {Button} this
30208              */
30209         'render': true
30210     });
30211     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30212 };
30213 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30214 //Roo.Toolbar.Item.prototype = {
30215     
30216     /**
30217      * Get this item's HTML Element
30218      * @return {HTMLElement}
30219      */
30220     getEl : function(){
30221        return this.el;  
30222     },
30223
30224     // private
30225     render : function(td){
30226         
30227          this.td = td;
30228         td.appendChild(this.el);
30229         
30230         this.fireEvent('render', this);
30231     },
30232     
30233     /**
30234      * Removes and destroys this item.
30235      */
30236     destroy : function(){
30237         this.td.parentNode.removeChild(this.td);
30238     },
30239     
30240     /**
30241      * Shows this item.
30242      */
30243     show: function(){
30244         this.hidden = false;
30245         this.td.style.display = "";
30246     },
30247     
30248     /**
30249      * Hides this item.
30250      */
30251     hide: function(){
30252         this.hidden = true;
30253         this.td.style.display = "none";
30254     },
30255     
30256     /**
30257      * Convenience function for boolean show/hide.
30258      * @param {Boolean} visible true to show/false to hide
30259      */
30260     setVisible: function(visible){
30261         if(visible) {
30262             this.show();
30263         }else{
30264             this.hide();
30265         }
30266     },
30267     
30268     /**
30269      * Try to focus this item.
30270      */
30271     focus : function(){
30272         Roo.fly(this.el).focus();
30273     },
30274     
30275     /**
30276      * Disables this item.
30277      */
30278     disable : function(){
30279         Roo.fly(this.td).addClass("x-item-disabled");
30280         this.disabled = true;
30281         this.el.disabled = true;
30282     },
30283     
30284     /**
30285      * Enables this item.
30286      */
30287     enable : function(){
30288         Roo.fly(this.td).removeClass("x-item-disabled");
30289         this.disabled = false;
30290         this.el.disabled = false;
30291     }
30292 });
30293
30294
30295 /**
30296  * @class Roo.Toolbar.Separator
30297  * @extends Roo.Toolbar.Item
30298  * A simple toolbar separator class
30299  * @constructor
30300  * Creates a new Separator
30301  */
30302 Roo.Toolbar.Separator = function(cfg){
30303     
30304     var s = document.createElement("span");
30305     s.className = "ytb-sep";
30306     if (cfg) {
30307         cfg.el = s;
30308     }
30309     
30310     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30311 };
30312 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30313     enable:Roo.emptyFn,
30314     disable:Roo.emptyFn,
30315     focus:Roo.emptyFn
30316 });
30317
30318 /**
30319  * @class Roo.Toolbar.Spacer
30320  * @extends Roo.Toolbar.Item
30321  * A simple element that adds extra horizontal space to a toolbar.
30322  * @constructor
30323  * Creates a new Spacer
30324  */
30325 Roo.Toolbar.Spacer = function(cfg){
30326     var s = document.createElement("div");
30327     s.className = "ytb-spacer";
30328     if (cfg) {
30329         cfg.el = s;
30330     }
30331     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30332 };
30333 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30334     enable:Roo.emptyFn,
30335     disable:Roo.emptyFn,
30336     focus:Roo.emptyFn
30337 });
30338
30339 /**
30340  * @class Roo.Toolbar.Fill
30341  * @extends Roo.Toolbar.Spacer
30342  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30343  * @constructor
30344  * Creates a new Spacer
30345  */
30346 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30347     // private
30348     render : function(td){
30349         td.style.width = '100%';
30350         Roo.Toolbar.Fill.superclass.render.call(this, td);
30351     }
30352 });
30353
30354 /**
30355  * @class Roo.Toolbar.TextItem
30356  * @extends Roo.Toolbar.Item
30357  * A simple class that renders text directly into a toolbar.
30358  * @constructor
30359  * Creates a new TextItem
30360  * @param {String} text
30361  */
30362 Roo.Toolbar.TextItem = function(cfg){
30363     var  text = cfg || "";
30364     if (typeof(cfg) == 'object') {
30365         text = cfg.text || "";
30366     }  else {
30367         cfg = null;
30368     }
30369     var s = document.createElement("span");
30370     s.className = "ytb-text";
30371     s.innerHTML = text;
30372     if (cfg) {
30373         cfg.el  = s;
30374     }
30375     
30376     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30377 };
30378 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30379     
30380      
30381     enable:Roo.emptyFn,
30382     disable:Roo.emptyFn,
30383     focus:Roo.emptyFn
30384 });
30385
30386 /**
30387  * @class Roo.Toolbar.Button
30388  * @extends Roo.Button
30389  * A button that renders into a toolbar.
30390  * @constructor
30391  * Creates a new Button
30392  * @param {Object} config A standard {@link Roo.Button} config object
30393  */
30394 Roo.Toolbar.Button = function(config){
30395     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30396 };
30397 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30398     render : function(td){
30399         this.td = td;
30400         Roo.Toolbar.Button.superclass.render.call(this, td);
30401     },
30402     
30403     /**
30404      * Removes and destroys this button
30405      */
30406     destroy : function(){
30407         Roo.Toolbar.Button.superclass.destroy.call(this);
30408         this.td.parentNode.removeChild(this.td);
30409     },
30410     
30411     /**
30412      * Shows this button
30413      */
30414     show: function(){
30415         this.hidden = false;
30416         this.td.style.display = "";
30417     },
30418     
30419     /**
30420      * Hides this button
30421      */
30422     hide: function(){
30423         this.hidden = true;
30424         this.td.style.display = "none";
30425     },
30426
30427     /**
30428      * Disables this item
30429      */
30430     disable : function(){
30431         Roo.fly(this.td).addClass("x-item-disabled");
30432         this.disabled = true;
30433     },
30434
30435     /**
30436      * Enables this item
30437      */
30438     enable : function(){
30439         Roo.fly(this.td).removeClass("x-item-disabled");
30440         this.disabled = false;
30441     }
30442 });
30443 // backwards compat
30444 Roo.ToolbarButton = Roo.Toolbar.Button;
30445
30446 /**
30447  * @class Roo.Toolbar.SplitButton
30448  * @extends Roo.SplitButton
30449  * A menu button that renders into a toolbar.
30450  * @constructor
30451  * Creates a new SplitButton
30452  * @param {Object} config A standard {@link Roo.SplitButton} config object
30453  */
30454 Roo.Toolbar.SplitButton = function(config){
30455     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30456 };
30457 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30458     render : function(td){
30459         this.td = td;
30460         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30461     },
30462     
30463     /**
30464      * Removes and destroys this button
30465      */
30466     destroy : function(){
30467         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30468         this.td.parentNode.removeChild(this.td);
30469     },
30470     
30471     /**
30472      * Shows this button
30473      */
30474     show: function(){
30475         this.hidden = false;
30476         this.td.style.display = "";
30477     },
30478     
30479     /**
30480      * Hides this button
30481      */
30482     hide: function(){
30483         this.hidden = true;
30484         this.td.style.display = "none";
30485     }
30486 });
30487
30488 // backwards compat
30489 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30490  * Based on:
30491  * Ext JS Library 1.1.1
30492  * Copyright(c) 2006-2007, Ext JS, LLC.
30493  *
30494  * Originally Released Under LGPL - original licence link has changed is not relivant.
30495  *
30496  * Fork - LGPL
30497  * <script type="text/javascript">
30498  */
30499  
30500 /**
30501  * @class Roo.PagingToolbar
30502  * @extends Roo.Toolbar
30503  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30504  * @constructor
30505  * Create a new PagingToolbar
30506  * @param {Object} config The config object
30507  */
30508 Roo.PagingToolbar = function(el, ds, config)
30509 {
30510     // old args format still supported... - xtype is prefered..
30511     if (typeof(el) == 'object' && el.xtype) {
30512         // created from xtype...
30513         config = el;
30514         ds = el.dataSource;
30515         el = config.container;
30516     }
30517     var items = [];
30518     if (config.items) {
30519         items = config.items;
30520         config.items = [];
30521     }
30522     
30523     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30524     this.ds = ds;
30525     this.cursor = 0;
30526     this.renderButtons(this.el);
30527     this.bind(ds);
30528     
30529     // supprot items array.
30530    
30531     Roo.each(items, function(e) {
30532         this.add(Roo.factory(e));
30533     },this);
30534     
30535 };
30536
30537 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30538     /**
30539      * @cfg {Roo.data.Store} dataSource
30540      * The underlying data store providing the paged data
30541      */
30542     /**
30543      * @cfg {String/HTMLElement/Element} container
30544      * container The id or element that will contain the toolbar
30545      */
30546     /**
30547      * @cfg {Boolean} displayInfo
30548      * True to display the displayMsg (defaults to false)
30549      */
30550     /**
30551      * @cfg {Number} pageSize
30552      * The number of records to display per page (defaults to 20)
30553      */
30554     pageSize: 20,
30555     /**
30556      * @cfg {String} displayMsg
30557      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30558      */
30559     displayMsg : 'Displaying {0} - {1} of {2}',
30560     /**
30561      * @cfg {String} emptyMsg
30562      * The message to display when no records are found (defaults to "No data to display")
30563      */
30564     emptyMsg : 'No data to display',
30565     /**
30566      * Customizable piece of the default paging text (defaults to "Page")
30567      * @type String
30568      */
30569     beforePageText : "Page",
30570     /**
30571      * Customizable piece of the default paging text (defaults to "of %0")
30572      * @type String
30573      */
30574     afterPageText : "of {0}",
30575     /**
30576      * Customizable piece of the default paging text (defaults to "First Page")
30577      * @type String
30578      */
30579     firstText : "First Page",
30580     /**
30581      * Customizable piece of the default paging text (defaults to "Previous Page")
30582      * @type String
30583      */
30584     prevText : "Previous Page",
30585     /**
30586      * Customizable piece of the default paging text (defaults to "Next Page")
30587      * @type String
30588      */
30589     nextText : "Next Page",
30590     /**
30591      * Customizable piece of the default paging text (defaults to "Last Page")
30592      * @type String
30593      */
30594     lastText : "Last Page",
30595     /**
30596      * Customizable piece of the default paging text (defaults to "Refresh")
30597      * @type String
30598      */
30599     refreshText : "Refresh",
30600
30601     // private
30602     renderButtons : function(el){
30603         Roo.PagingToolbar.superclass.render.call(this, el);
30604         this.first = this.addButton({
30605             tooltip: this.firstText,
30606             cls: "x-btn-icon x-grid-page-first",
30607             disabled: true,
30608             handler: this.onClick.createDelegate(this, ["first"])
30609         });
30610         this.prev = this.addButton({
30611             tooltip: this.prevText,
30612             cls: "x-btn-icon x-grid-page-prev",
30613             disabled: true,
30614             handler: this.onClick.createDelegate(this, ["prev"])
30615         });
30616         //this.addSeparator();
30617         this.add(this.beforePageText);
30618         this.field = Roo.get(this.addDom({
30619            tag: "input",
30620            type: "text",
30621            size: "3",
30622            value: "1",
30623            cls: "x-grid-page-number"
30624         }).el);
30625         this.field.on("keydown", this.onPagingKeydown, this);
30626         this.field.on("focus", function(){this.dom.select();});
30627         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30628         this.field.setHeight(18);
30629         //this.addSeparator();
30630         this.next = this.addButton({
30631             tooltip: this.nextText,
30632             cls: "x-btn-icon x-grid-page-next",
30633             disabled: true,
30634             handler: this.onClick.createDelegate(this, ["next"])
30635         });
30636         this.last = this.addButton({
30637             tooltip: this.lastText,
30638             cls: "x-btn-icon x-grid-page-last",
30639             disabled: true,
30640             handler: this.onClick.createDelegate(this, ["last"])
30641         });
30642         //this.addSeparator();
30643         this.loading = this.addButton({
30644             tooltip: this.refreshText,
30645             cls: "x-btn-icon x-grid-loading",
30646             handler: this.onClick.createDelegate(this, ["refresh"])
30647         });
30648
30649         if(this.displayInfo){
30650             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30651         }
30652     },
30653
30654     // private
30655     updateInfo : function(){
30656         if(this.displayEl){
30657             var count = this.ds.getCount();
30658             var msg = count == 0 ?
30659                 this.emptyMsg :
30660                 String.format(
30661                     this.displayMsg,
30662                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30663                 );
30664             this.displayEl.update(msg);
30665         }
30666     },
30667
30668     // private
30669     onLoad : function(ds, r, o){
30670        this.cursor = o.params ? o.params.start : 0;
30671        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30672
30673        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30674        this.field.dom.value = ap;
30675        this.first.setDisabled(ap == 1);
30676        this.prev.setDisabled(ap == 1);
30677        this.next.setDisabled(ap == ps);
30678        this.last.setDisabled(ap == ps);
30679        this.loading.enable();
30680        this.updateInfo();
30681     },
30682
30683     // private
30684     getPageData : function(){
30685         var total = this.ds.getTotalCount();
30686         return {
30687             total : total,
30688             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30689             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30690         };
30691     },
30692
30693     // private
30694     onLoadError : function(){
30695         this.loading.enable();
30696     },
30697
30698     // private
30699     onPagingKeydown : function(e){
30700         var k = e.getKey();
30701         var d = this.getPageData();
30702         if(k == e.RETURN){
30703             var v = this.field.dom.value, pageNum;
30704             if(!v || isNaN(pageNum = parseInt(v, 10))){
30705                 this.field.dom.value = d.activePage;
30706                 return;
30707             }
30708             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30709             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30710             e.stopEvent();
30711         }
30712         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))
30713         {
30714           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30715           this.field.dom.value = pageNum;
30716           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30717           e.stopEvent();
30718         }
30719         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30720         {
30721           var v = this.field.dom.value, pageNum; 
30722           var increment = (e.shiftKey) ? 10 : 1;
30723           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30724             increment *= -1;
30725           }
30726           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30727             this.field.dom.value = d.activePage;
30728             return;
30729           }
30730           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30731           {
30732             this.field.dom.value = parseInt(v, 10) + increment;
30733             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30734             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30735           }
30736           e.stopEvent();
30737         }
30738     },
30739
30740     // private
30741     beforeLoad : function(){
30742         if(this.loading){
30743             this.loading.disable();
30744         }
30745     },
30746
30747     // private
30748     onClick : function(which){
30749         var ds = this.ds;
30750         switch(which){
30751             case "first":
30752                 ds.load({params:{start: 0, limit: this.pageSize}});
30753             break;
30754             case "prev":
30755                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30756             break;
30757             case "next":
30758                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30759             break;
30760             case "last":
30761                 var total = ds.getTotalCount();
30762                 var extra = total % this.pageSize;
30763                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30764                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30765             break;
30766             case "refresh":
30767                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30768             break;
30769         }
30770     },
30771
30772     /**
30773      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30774      * @param {Roo.data.Store} store The data store to unbind
30775      */
30776     unbind : function(ds){
30777         ds.un("beforeload", this.beforeLoad, this);
30778         ds.un("load", this.onLoad, this);
30779         ds.un("loadexception", this.onLoadError, this);
30780         ds.un("remove", this.updateInfo, this);
30781         ds.un("add", this.updateInfo, this);
30782         this.ds = undefined;
30783     },
30784
30785     /**
30786      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30787      * @param {Roo.data.Store} store The data store to bind
30788      */
30789     bind : function(ds){
30790         ds.on("beforeload", this.beforeLoad, this);
30791         ds.on("load", this.onLoad, this);
30792         ds.on("loadexception", this.onLoadError, this);
30793         ds.on("remove", this.updateInfo, this);
30794         ds.on("add", this.updateInfo, this);
30795         this.ds = ds;
30796     }
30797 });/*
30798  * Based on:
30799  * Ext JS Library 1.1.1
30800  * Copyright(c) 2006-2007, Ext JS, LLC.
30801  *
30802  * Originally Released Under LGPL - original licence link has changed is not relivant.
30803  *
30804  * Fork - LGPL
30805  * <script type="text/javascript">
30806  */
30807
30808 /**
30809  * @class Roo.Resizable
30810  * @extends Roo.util.Observable
30811  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30812  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30813  * 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
30814  * the element will be wrapped for you automatically.</p>
30815  * <p>Here is the list of valid resize handles:</p>
30816  * <pre>
30817 Value   Description
30818 ------  -------------------
30819  'n'     north
30820  's'     south
30821  'e'     east
30822  'w'     west
30823  'nw'    northwest
30824  'sw'    southwest
30825  'se'    southeast
30826  'ne'    northeast
30827  'hd'    horizontal drag
30828  'all'   all
30829 </pre>
30830  * <p>Here's an example showing the creation of a typical Resizable:</p>
30831  * <pre><code>
30832 var resizer = new Roo.Resizable("element-id", {
30833     handles: 'all',
30834     minWidth: 200,
30835     minHeight: 100,
30836     maxWidth: 500,
30837     maxHeight: 400,
30838     pinned: true
30839 });
30840 resizer.on("resize", myHandler);
30841 </code></pre>
30842  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30843  * resizer.east.setDisplayed(false);</p>
30844  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30845  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30846  * resize operation's new size (defaults to [0, 0])
30847  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30848  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30849  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30850  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30851  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30852  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30853  * @cfg {Number} width The width of the element in pixels (defaults to null)
30854  * @cfg {Number} height The height of the element in pixels (defaults to null)
30855  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30856  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30857  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30858  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30859  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30860  * in favor of the handles config option (defaults to false)
30861  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30862  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30863  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30864  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30865  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30866  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30867  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30868  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30869  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30870  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30871  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30872  * @constructor
30873  * Create a new resizable component
30874  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30875  * @param {Object} config configuration options
30876   */
30877 Roo.Resizable = function(el, config)
30878 {
30879     this.el = Roo.get(el);
30880
30881     if(config && config.wrap){
30882         config.resizeChild = this.el;
30883         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30884         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30885         this.el.setStyle("overflow", "hidden");
30886         this.el.setPositioning(config.resizeChild.getPositioning());
30887         config.resizeChild.clearPositioning();
30888         if(!config.width || !config.height){
30889             var csize = config.resizeChild.getSize();
30890             this.el.setSize(csize.width, csize.height);
30891         }
30892         if(config.pinned && !config.adjustments){
30893             config.adjustments = "auto";
30894         }
30895     }
30896
30897     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30898     this.proxy.unselectable();
30899     this.proxy.enableDisplayMode('block');
30900
30901     Roo.apply(this, config);
30902
30903     if(this.pinned){
30904         this.disableTrackOver = true;
30905         this.el.addClass("x-resizable-pinned");
30906     }
30907     // if the element isn't positioned, make it relative
30908     var position = this.el.getStyle("position");
30909     if(position != "absolute" && position != "fixed"){
30910         this.el.setStyle("position", "relative");
30911     }
30912     if(!this.handles){ // no handles passed, must be legacy style
30913         this.handles = 's,e,se';
30914         if(this.multiDirectional){
30915             this.handles += ',n,w';
30916         }
30917     }
30918     if(this.handles == "all"){
30919         this.handles = "n s e w ne nw se sw";
30920     }
30921     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30922     var ps = Roo.Resizable.positions;
30923     for(var i = 0, len = hs.length; i < len; i++){
30924         if(hs[i] && ps[hs[i]]){
30925             var pos = ps[hs[i]];
30926             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30927         }
30928     }
30929     // legacy
30930     this.corner = this.southeast;
30931     
30932     // updateBox = the box can move..
30933     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30934         this.updateBox = true;
30935     }
30936
30937     this.activeHandle = null;
30938
30939     if(this.resizeChild){
30940         if(typeof this.resizeChild == "boolean"){
30941             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30942         }else{
30943             this.resizeChild = Roo.get(this.resizeChild, true);
30944         }
30945     }
30946     
30947     if(this.adjustments == "auto"){
30948         var rc = this.resizeChild;
30949         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30950         if(rc && (hw || hn)){
30951             rc.position("relative");
30952             rc.setLeft(hw ? hw.el.getWidth() : 0);
30953             rc.setTop(hn ? hn.el.getHeight() : 0);
30954         }
30955         this.adjustments = [
30956             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30957             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30958         ];
30959     }
30960
30961     if(this.draggable){
30962         this.dd = this.dynamic ?
30963             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30964         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30965     }
30966
30967     // public events
30968     this.addEvents({
30969         /**
30970          * @event beforeresize
30971          * Fired before resize is allowed. Set enabled to false to cancel resize.
30972          * @param {Roo.Resizable} this
30973          * @param {Roo.EventObject} e The mousedown event
30974          */
30975         "beforeresize" : true,
30976         /**
30977          * @event resizing
30978          * Fired a resizing.
30979          * @param {Roo.Resizable} this
30980          * @param {Number} x The new x position
30981          * @param {Number} y The new y position
30982          * @param {Number} w The new w width
30983          * @param {Number} h The new h hight
30984          * @param {Roo.EventObject} e The mouseup event
30985          */
30986         "resizing" : true,
30987         /**
30988          * @event resize
30989          * Fired after a resize.
30990          * @param {Roo.Resizable} this
30991          * @param {Number} width The new width
30992          * @param {Number} height The new height
30993          * @param {Roo.EventObject} e The mouseup event
30994          */
30995         "resize" : true
30996     });
30997
30998     if(this.width !== null && this.height !== null){
30999         this.resizeTo(this.width, this.height);
31000     }else{
31001         this.updateChildSize();
31002     }
31003     if(Roo.isIE){
31004         this.el.dom.style.zoom = 1;
31005     }
31006     Roo.Resizable.superclass.constructor.call(this);
31007 };
31008
31009 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31010         resizeChild : false,
31011         adjustments : [0, 0],
31012         minWidth : 5,
31013         minHeight : 5,
31014         maxWidth : 10000,
31015         maxHeight : 10000,
31016         enabled : true,
31017         animate : false,
31018         duration : .35,
31019         dynamic : false,
31020         handles : false,
31021         multiDirectional : false,
31022         disableTrackOver : false,
31023         easing : 'easeOutStrong',
31024         widthIncrement : 0,
31025         heightIncrement : 0,
31026         pinned : false,
31027         width : null,
31028         height : null,
31029         preserveRatio : false,
31030         transparent: false,
31031         minX: 0,
31032         minY: 0,
31033         draggable: false,
31034
31035         /**
31036          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31037          */
31038         constrainTo: undefined,
31039         /**
31040          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31041          */
31042         resizeRegion: undefined,
31043
31044
31045     /**
31046      * Perform a manual resize
31047      * @param {Number} width
31048      * @param {Number} height
31049      */
31050     resizeTo : function(width, height){
31051         this.el.setSize(width, height);
31052         this.updateChildSize();
31053         this.fireEvent("resize", this, width, height, null);
31054     },
31055
31056     // private
31057     startSizing : function(e, handle){
31058         this.fireEvent("beforeresize", this, e);
31059         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31060
31061             if(!this.overlay){
31062                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31063                 this.overlay.unselectable();
31064                 this.overlay.enableDisplayMode("block");
31065                 this.overlay.on("mousemove", this.onMouseMove, this);
31066                 this.overlay.on("mouseup", this.onMouseUp, this);
31067             }
31068             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31069
31070             this.resizing = true;
31071             this.startBox = this.el.getBox();
31072             this.startPoint = e.getXY();
31073             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31074                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31075
31076             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31077             this.overlay.show();
31078
31079             if(this.constrainTo) {
31080                 var ct = Roo.get(this.constrainTo);
31081                 this.resizeRegion = ct.getRegion().adjust(
31082                     ct.getFrameWidth('t'),
31083                     ct.getFrameWidth('l'),
31084                     -ct.getFrameWidth('b'),
31085                     -ct.getFrameWidth('r')
31086                 );
31087             }
31088
31089             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31090             this.proxy.show();
31091             this.proxy.setBox(this.startBox);
31092             if(!this.dynamic){
31093                 this.proxy.setStyle('visibility', 'visible');
31094             }
31095         }
31096     },
31097
31098     // private
31099     onMouseDown : function(handle, e){
31100         if(this.enabled){
31101             e.stopEvent();
31102             this.activeHandle = handle;
31103             this.startSizing(e, handle);
31104         }
31105     },
31106
31107     // private
31108     onMouseUp : function(e){
31109         var size = this.resizeElement();
31110         this.resizing = false;
31111         this.handleOut();
31112         this.overlay.hide();
31113         this.proxy.hide();
31114         this.fireEvent("resize", this, size.width, size.height, e);
31115     },
31116
31117     // private
31118     updateChildSize : function(){
31119         
31120         if(this.resizeChild){
31121             var el = this.el;
31122             var child = this.resizeChild;
31123             var adj = this.adjustments;
31124             if(el.dom.offsetWidth){
31125                 var b = el.getSize(true);
31126                 child.setSize(b.width+adj[0], b.height+adj[1]);
31127             }
31128             // Second call here for IE
31129             // The first call enables instant resizing and
31130             // the second call corrects scroll bars if they
31131             // exist
31132             if(Roo.isIE){
31133                 setTimeout(function(){
31134                     if(el.dom.offsetWidth){
31135                         var b = el.getSize(true);
31136                         child.setSize(b.width+adj[0], b.height+adj[1]);
31137                     }
31138                 }, 10);
31139             }
31140         }
31141     },
31142
31143     // private
31144     snap : function(value, inc, min){
31145         if(!inc || !value) {
31146             return value;
31147         }
31148         var newValue = value;
31149         var m = value % inc;
31150         if(m > 0){
31151             if(m > (inc/2)){
31152                 newValue = value + (inc-m);
31153             }else{
31154                 newValue = value - m;
31155             }
31156         }
31157         return Math.max(min, newValue);
31158     },
31159
31160     // private
31161     resizeElement : function(){
31162         var box = this.proxy.getBox();
31163         if(this.updateBox){
31164             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31165         }else{
31166             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31167         }
31168         this.updateChildSize();
31169         if(!this.dynamic){
31170             this.proxy.hide();
31171         }
31172         return box;
31173     },
31174
31175     // private
31176     constrain : function(v, diff, m, mx){
31177         if(v - diff < m){
31178             diff = v - m;
31179         }else if(v - diff > mx){
31180             diff = mx - v;
31181         }
31182         return diff;
31183     },
31184
31185     // private
31186     onMouseMove : function(e){
31187         
31188         if(this.enabled){
31189             try{// try catch so if something goes wrong the user doesn't get hung
31190
31191             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31192                 return;
31193             }
31194
31195             //var curXY = this.startPoint;
31196             var curSize = this.curSize || this.startBox;
31197             var x = this.startBox.x, y = this.startBox.y;
31198             var ox = x, oy = y;
31199             var w = curSize.width, h = curSize.height;
31200             var ow = w, oh = h;
31201             var mw = this.minWidth, mh = this.minHeight;
31202             var mxw = this.maxWidth, mxh = this.maxHeight;
31203             var wi = this.widthIncrement;
31204             var hi = this.heightIncrement;
31205
31206             var eventXY = e.getXY();
31207             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31208             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31209
31210             var pos = this.activeHandle.position;
31211
31212             switch(pos){
31213                 case "east":
31214                     w += diffX;
31215                     w = Math.min(Math.max(mw, w), mxw);
31216                     break;
31217              
31218                 case "south":
31219                     h += diffY;
31220                     h = Math.min(Math.max(mh, h), mxh);
31221                     break;
31222                 case "southeast":
31223                     w += diffX;
31224                     h += diffY;
31225                     w = Math.min(Math.max(mw, w), mxw);
31226                     h = Math.min(Math.max(mh, h), mxh);
31227                     break;
31228                 case "north":
31229                     diffY = this.constrain(h, diffY, mh, mxh);
31230                     y += diffY;
31231                     h -= diffY;
31232                     break;
31233                 case "hdrag":
31234                     
31235                     if (wi) {
31236                         var adiffX = Math.abs(diffX);
31237                         var sub = (adiffX % wi); // how much 
31238                         if (sub > (wi/2)) { // far enough to snap
31239                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31240                         } else {
31241                             // remove difference.. 
31242                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31243                         }
31244                     }
31245                     x += diffX;
31246                     x = Math.max(this.minX, x);
31247                     break;
31248                 case "west":
31249                     diffX = this.constrain(w, diffX, mw, mxw);
31250                     x += diffX;
31251                     w -= diffX;
31252                     break;
31253                 case "northeast":
31254                     w += diffX;
31255                     w = Math.min(Math.max(mw, w), mxw);
31256                     diffY = this.constrain(h, diffY, mh, mxh);
31257                     y += diffY;
31258                     h -= diffY;
31259                     break;
31260                 case "northwest":
31261                     diffX = this.constrain(w, diffX, mw, mxw);
31262                     diffY = this.constrain(h, diffY, mh, mxh);
31263                     y += diffY;
31264                     h -= diffY;
31265                     x += diffX;
31266                     w -= diffX;
31267                     break;
31268                case "southwest":
31269                     diffX = this.constrain(w, diffX, mw, mxw);
31270                     h += diffY;
31271                     h = Math.min(Math.max(mh, h), mxh);
31272                     x += diffX;
31273                     w -= diffX;
31274                     break;
31275             }
31276
31277             var sw = this.snap(w, wi, mw);
31278             var sh = this.snap(h, hi, mh);
31279             if(sw != w || sh != h){
31280                 switch(pos){
31281                     case "northeast":
31282                         y -= sh - h;
31283                     break;
31284                     case "north":
31285                         y -= sh - h;
31286                         break;
31287                     case "southwest":
31288                         x -= sw - w;
31289                     break;
31290                     case "west":
31291                         x -= sw - w;
31292                         break;
31293                     case "northwest":
31294                         x -= sw - w;
31295                         y -= sh - h;
31296                     break;
31297                 }
31298                 w = sw;
31299                 h = sh;
31300             }
31301
31302             if(this.preserveRatio){
31303                 switch(pos){
31304                     case "southeast":
31305                     case "east":
31306                         h = oh * (w/ow);
31307                         h = Math.min(Math.max(mh, h), mxh);
31308                         w = ow * (h/oh);
31309                        break;
31310                     case "south":
31311                         w = ow * (h/oh);
31312                         w = Math.min(Math.max(mw, w), mxw);
31313                         h = oh * (w/ow);
31314                         break;
31315                     case "northeast":
31316                         w = ow * (h/oh);
31317                         w = Math.min(Math.max(mw, w), mxw);
31318                         h = oh * (w/ow);
31319                     break;
31320                     case "north":
31321                         var tw = w;
31322                         w = ow * (h/oh);
31323                         w = Math.min(Math.max(mw, w), mxw);
31324                         h = oh * (w/ow);
31325                         x += (tw - w) / 2;
31326                         break;
31327                     case "southwest":
31328                         h = oh * (w/ow);
31329                         h = Math.min(Math.max(mh, h), mxh);
31330                         var tw = w;
31331                         w = ow * (h/oh);
31332                         x += tw - w;
31333                         break;
31334                     case "west":
31335                         var th = h;
31336                         h = oh * (w/ow);
31337                         h = Math.min(Math.max(mh, h), mxh);
31338                         y += (th - h) / 2;
31339                         var tw = w;
31340                         w = ow * (h/oh);
31341                         x += tw - w;
31342                        break;
31343                     case "northwest":
31344                         var tw = w;
31345                         var th = h;
31346                         h = oh * (w/ow);
31347                         h = Math.min(Math.max(mh, h), mxh);
31348                         w = ow * (h/oh);
31349                         y += th - h;
31350                         x += tw - w;
31351                        break;
31352
31353                 }
31354             }
31355             if (pos == 'hdrag') {
31356                 w = ow;
31357             }
31358             this.proxy.setBounds(x, y, w, h);
31359             if(this.dynamic){
31360                 this.resizeElement();
31361             }
31362             }catch(e){}
31363         }
31364         this.fireEvent("resizing", this, x, y, w, h, e);
31365     },
31366
31367     // private
31368     handleOver : function(){
31369         if(this.enabled){
31370             this.el.addClass("x-resizable-over");
31371         }
31372     },
31373
31374     // private
31375     handleOut : function(){
31376         if(!this.resizing){
31377             this.el.removeClass("x-resizable-over");
31378         }
31379     },
31380
31381     /**
31382      * Returns the element this component is bound to.
31383      * @return {Roo.Element}
31384      */
31385     getEl : function(){
31386         return this.el;
31387     },
31388
31389     /**
31390      * Returns the resizeChild element (or null).
31391      * @return {Roo.Element}
31392      */
31393     getResizeChild : function(){
31394         return this.resizeChild;
31395     },
31396     groupHandler : function()
31397     {
31398         
31399     },
31400     /**
31401      * Destroys this resizable. If the element was wrapped and
31402      * removeEl is not true then the element remains.
31403      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31404      */
31405     destroy : function(removeEl){
31406         this.proxy.remove();
31407         if(this.overlay){
31408             this.overlay.removeAllListeners();
31409             this.overlay.remove();
31410         }
31411         var ps = Roo.Resizable.positions;
31412         for(var k in ps){
31413             if(typeof ps[k] != "function" && this[ps[k]]){
31414                 var h = this[ps[k]];
31415                 h.el.removeAllListeners();
31416                 h.el.remove();
31417             }
31418         }
31419         if(removeEl){
31420             this.el.update("");
31421             this.el.remove();
31422         }
31423     }
31424 });
31425
31426 // private
31427 // hash to map config positions to true positions
31428 Roo.Resizable.positions = {
31429     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31430     hd: "hdrag"
31431 };
31432
31433 // private
31434 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31435     if(!this.tpl){
31436         // only initialize the template if resizable is used
31437         var tpl = Roo.DomHelper.createTemplate(
31438             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31439         );
31440         tpl.compile();
31441         Roo.Resizable.Handle.prototype.tpl = tpl;
31442     }
31443     this.position = pos;
31444     this.rz = rz;
31445     // show north drag fro topdra
31446     var handlepos = pos == 'hdrag' ? 'north' : pos;
31447     
31448     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31449     if (pos == 'hdrag') {
31450         this.el.setStyle('cursor', 'pointer');
31451     }
31452     this.el.unselectable();
31453     if(transparent){
31454         this.el.setOpacity(0);
31455     }
31456     this.el.on("mousedown", this.onMouseDown, this);
31457     if(!disableTrackOver){
31458         this.el.on("mouseover", this.onMouseOver, this);
31459         this.el.on("mouseout", this.onMouseOut, this);
31460     }
31461 };
31462
31463 // private
31464 Roo.Resizable.Handle.prototype = {
31465     afterResize : function(rz){
31466         Roo.log('after?');
31467         // do nothing
31468     },
31469     // private
31470     onMouseDown : function(e){
31471         this.rz.onMouseDown(this, e);
31472     },
31473     // private
31474     onMouseOver : function(e){
31475         this.rz.handleOver(this, e);
31476     },
31477     // private
31478     onMouseOut : function(e){
31479         this.rz.handleOut(this, e);
31480     }
31481 };/*
31482  * Based on:
31483  * Ext JS Library 1.1.1
31484  * Copyright(c) 2006-2007, Ext JS, LLC.
31485  *
31486  * Originally Released Under LGPL - original licence link has changed is not relivant.
31487  *
31488  * Fork - LGPL
31489  * <script type="text/javascript">
31490  */
31491
31492 /**
31493  * @class Roo.Editor
31494  * @extends Roo.Component
31495  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31496  * @constructor
31497  * Create a new Editor
31498  * @param {Roo.form.Field} field The Field object (or descendant)
31499  * @param {Object} config The config object
31500  */
31501 Roo.Editor = function(field, config){
31502     Roo.Editor.superclass.constructor.call(this, config);
31503     this.field = field;
31504     this.addEvents({
31505         /**
31506              * @event beforestartedit
31507              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31508              * false from the handler of this event.
31509              * @param {Editor} this
31510              * @param {Roo.Element} boundEl The underlying element bound to this editor
31511              * @param {Mixed} value The field value being set
31512              */
31513         "beforestartedit" : true,
31514         /**
31515              * @event startedit
31516              * Fires when this editor is displayed
31517              * @param {Roo.Element} boundEl The underlying element bound to this editor
31518              * @param {Mixed} value The starting field value
31519              */
31520         "startedit" : true,
31521         /**
31522              * @event beforecomplete
31523              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31524              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31525              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31526              * event will not fire since no edit actually occurred.
31527              * @param {Editor} this
31528              * @param {Mixed} value The current field value
31529              * @param {Mixed} startValue The original field value
31530              */
31531         "beforecomplete" : true,
31532         /**
31533              * @event complete
31534              * Fires after editing is complete and any changed value has been written to the underlying field.
31535              * @param {Editor} this
31536              * @param {Mixed} value The current field value
31537              * @param {Mixed} startValue The original field value
31538              */
31539         "complete" : true,
31540         /**
31541          * @event specialkey
31542          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31543          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31544          * @param {Roo.form.Field} this
31545          * @param {Roo.EventObject} e The event object
31546          */
31547         "specialkey" : true
31548     });
31549 };
31550
31551 Roo.extend(Roo.Editor, Roo.Component, {
31552     /**
31553      * @cfg {Boolean/String} autosize
31554      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31555      * or "height" to adopt the height only (defaults to false)
31556      */
31557     /**
31558      * @cfg {Boolean} revertInvalid
31559      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31560      * validation fails (defaults to true)
31561      */
31562     /**
31563      * @cfg {Boolean} ignoreNoChange
31564      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31565      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31566      * will never be ignored.
31567      */
31568     /**
31569      * @cfg {Boolean} hideEl
31570      * False to keep the bound element visible while the editor is displayed (defaults to true)
31571      */
31572     /**
31573      * @cfg {Mixed} value
31574      * The data value of the underlying field (defaults to "")
31575      */
31576     value : "",
31577     /**
31578      * @cfg {String} alignment
31579      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31580      */
31581     alignment: "c-c?",
31582     /**
31583      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31584      * for bottom-right shadow (defaults to "frame")
31585      */
31586     shadow : "frame",
31587     /**
31588      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31589      */
31590     constrain : false,
31591     /**
31592      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31593      */
31594     completeOnEnter : false,
31595     /**
31596      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31597      */
31598     cancelOnEsc : false,
31599     /**
31600      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31601      */
31602     updateEl : false,
31603
31604     // private
31605     onRender : function(ct, position){
31606         this.el = new Roo.Layer({
31607             shadow: this.shadow,
31608             cls: "x-editor",
31609             parentEl : ct,
31610             shim : this.shim,
31611             shadowOffset:4,
31612             id: this.id,
31613             constrain: this.constrain
31614         });
31615         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31616         if(this.field.msgTarget != 'title'){
31617             this.field.msgTarget = 'qtip';
31618         }
31619         this.field.render(this.el);
31620         if(Roo.isGecko){
31621             this.field.el.dom.setAttribute('autocomplete', 'off');
31622         }
31623         this.field.on("specialkey", this.onSpecialKey, this);
31624         if(this.swallowKeys){
31625             this.field.el.swallowEvent(['keydown','keypress']);
31626         }
31627         this.field.show();
31628         this.field.on("blur", this.onBlur, this);
31629         if(this.field.grow){
31630             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31631         }
31632     },
31633
31634     onSpecialKey : function(field, e)
31635     {
31636         //Roo.log('editor onSpecialKey');
31637         if(this.completeOnEnter && e.getKey() == e.ENTER){
31638             e.stopEvent();
31639             this.completeEdit();
31640             return;
31641         }
31642         // do not fire special key otherwise it might hide close the editor...
31643         if(e.getKey() == e.ENTER){    
31644             return;
31645         }
31646         if(this.cancelOnEsc && e.getKey() == e.ESC){
31647             this.cancelEdit();
31648             return;
31649         } 
31650         this.fireEvent('specialkey', field, e);
31651     
31652     },
31653
31654     /**
31655      * Starts the editing process and shows the editor.
31656      * @param {String/HTMLElement/Element} el The element to edit
31657      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31658       * to the innerHTML of el.
31659      */
31660     startEdit : function(el, value){
31661         if(this.editing){
31662             this.completeEdit();
31663         }
31664         this.boundEl = Roo.get(el);
31665         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31666         if(!this.rendered){
31667             this.render(this.parentEl || document.body);
31668         }
31669         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31670             return;
31671         }
31672         this.startValue = v;
31673         this.field.setValue(v);
31674         if(this.autoSize){
31675             var sz = this.boundEl.getSize();
31676             switch(this.autoSize){
31677                 case "width":
31678                 this.setSize(sz.width,  "");
31679                 break;
31680                 case "height":
31681                 this.setSize("",  sz.height);
31682                 break;
31683                 default:
31684                 this.setSize(sz.width,  sz.height);
31685             }
31686         }
31687         this.el.alignTo(this.boundEl, this.alignment);
31688         this.editing = true;
31689         if(Roo.QuickTips){
31690             Roo.QuickTips.disable();
31691         }
31692         this.show();
31693     },
31694
31695     /**
31696      * Sets the height and width of this editor.
31697      * @param {Number} width The new width
31698      * @param {Number} height The new height
31699      */
31700     setSize : function(w, h){
31701         this.field.setSize(w, h);
31702         if(this.el){
31703             this.el.sync();
31704         }
31705     },
31706
31707     /**
31708      * Realigns the editor to the bound field based on the current alignment config value.
31709      */
31710     realign : function(){
31711         this.el.alignTo(this.boundEl, this.alignment);
31712     },
31713
31714     /**
31715      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31716      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31717      */
31718     completeEdit : function(remainVisible){
31719         if(!this.editing){
31720             return;
31721         }
31722         var v = this.getValue();
31723         if(this.revertInvalid !== false && !this.field.isValid()){
31724             v = this.startValue;
31725             this.cancelEdit(true);
31726         }
31727         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31728             this.editing = false;
31729             this.hide();
31730             return;
31731         }
31732         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31733             this.editing = false;
31734             if(this.updateEl && this.boundEl){
31735                 this.boundEl.update(v);
31736             }
31737             if(remainVisible !== true){
31738                 this.hide();
31739             }
31740             this.fireEvent("complete", this, v, this.startValue);
31741         }
31742     },
31743
31744     // private
31745     onShow : function(){
31746         this.el.show();
31747         if(this.hideEl !== false){
31748             this.boundEl.hide();
31749         }
31750         this.field.show();
31751         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31752             this.fixIEFocus = true;
31753             this.deferredFocus.defer(50, this);
31754         }else{
31755             this.field.focus();
31756         }
31757         this.fireEvent("startedit", this.boundEl, this.startValue);
31758     },
31759
31760     deferredFocus : function(){
31761         if(this.editing){
31762             this.field.focus();
31763         }
31764     },
31765
31766     /**
31767      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31768      * reverted to the original starting value.
31769      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31770      * cancel (defaults to false)
31771      */
31772     cancelEdit : function(remainVisible){
31773         if(this.editing){
31774             this.setValue(this.startValue);
31775             if(remainVisible !== true){
31776                 this.hide();
31777             }
31778         }
31779     },
31780
31781     // private
31782     onBlur : function(){
31783         if(this.allowBlur !== true && this.editing){
31784             this.completeEdit();
31785         }
31786     },
31787
31788     // private
31789     onHide : function(){
31790         if(this.editing){
31791             this.completeEdit();
31792             return;
31793         }
31794         this.field.blur();
31795         if(this.field.collapse){
31796             this.field.collapse();
31797         }
31798         this.el.hide();
31799         if(this.hideEl !== false){
31800             this.boundEl.show();
31801         }
31802         if(Roo.QuickTips){
31803             Roo.QuickTips.enable();
31804         }
31805     },
31806
31807     /**
31808      * Sets the data value of the editor
31809      * @param {Mixed} value Any valid value supported by the underlying field
31810      */
31811     setValue : function(v){
31812         this.field.setValue(v);
31813     },
31814
31815     /**
31816      * Gets the data value of the editor
31817      * @return {Mixed} The data value
31818      */
31819     getValue : function(){
31820         return this.field.getValue();
31821     }
31822 });/*
31823  * Based on:
31824  * Ext JS Library 1.1.1
31825  * Copyright(c) 2006-2007, Ext JS, LLC.
31826  *
31827  * Originally Released Under LGPL - original licence link has changed is not relivant.
31828  *
31829  * Fork - LGPL
31830  * <script type="text/javascript">
31831  */
31832  
31833 /**
31834  * @class Roo.BasicDialog
31835  * @extends Roo.util.Observable
31836  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31837  * <pre><code>
31838 var dlg = new Roo.BasicDialog("my-dlg", {
31839     height: 200,
31840     width: 300,
31841     minHeight: 100,
31842     minWidth: 150,
31843     modal: true,
31844     proxyDrag: true,
31845     shadow: true
31846 });
31847 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31848 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31849 dlg.addButton('Cancel', dlg.hide, dlg);
31850 dlg.show();
31851 </code></pre>
31852   <b>A Dialog should always be a direct child of the body element.</b>
31853  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31854  * @cfg {String} title Default text to display in the title bar (defaults to null)
31855  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31856  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31857  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31858  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31859  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31860  * (defaults to null with no animation)
31861  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31862  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31863  * property for valid values (defaults to 'all')
31864  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31865  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31866  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31867  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31868  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31869  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31870  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31871  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31872  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31873  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31874  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31875  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31876  * draggable = true (defaults to false)
31877  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31878  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31879  * shadow (defaults to false)
31880  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31881  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31882  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31883  * @cfg {Array} buttons Array of buttons
31884  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31885  * @constructor
31886  * Create a new BasicDialog.
31887  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31888  * @param {Object} config Configuration options
31889  */
31890 Roo.BasicDialog = function(el, config){
31891     this.el = Roo.get(el);
31892     var dh = Roo.DomHelper;
31893     if(!this.el && config && config.autoCreate){
31894         if(typeof config.autoCreate == "object"){
31895             if(!config.autoCreate.id){
31896                 config.autoCreate.id = el;
31897             }
31898             this.el = dh.append(document.body,
31899                         config.autoCreate, true);
31900         }else{
31901             this.el = dh.append(document.body,
31902                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31903         }
31904     }
31905     el = this.el;
31906     el.setDisplayed(true);
31907     el.hide = this.hideAction;
31908     this.id = el.id;
31909     el.addClass("x-dlg");
31910
31911     Roo.apply(this, config);
31912
31913     this.proxy = el.createProxy("x-dlg-proxy");
31914     this.proxy.hide = this.hideAction;
31915     this.proxy.setOpacity(.5);
31916     this.proxy.hide();
31917
31918     if(config.width){
31919         el.setWidth(config.width);
31920     }
31921     if(config.height){
31922         el.setHeight(config.height);
31923     }
31924     this.size = el.getSize();
31925     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31926         this.xy = [config.x,config.y];
31927     }else{
31928         this.xy = el.getCenterXY(true);
31929     }
31930     /** The header element @type Roo.Element */
31931     this.header = el.child("> .x-dlg-hd");
31932     /** The body element @type Roo.Element */
31933     this.body = el.child("> .x-dlg-bd");
31934     /** The footer element @type Roo.Element */
31935     this.footer = el.child("> .x-dlg-ft");
31936
31937     if(!this.header){
31938         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31939     }
31940     if(!this.body){
31941         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31942     }
31943
31944     this.header.unselectable();
31945     if(this.title){
31946         this.header.update(this.title);
31947     }
31948     // this element allows the dialog to be focused for keyboard event
31949     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31950     this.focusEl.swallowEvent("click", true);
31951
31952     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31953
31954     // wrap the body and footer for special rendering
31955     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31956     if(this.footer){
31957         this.bwrap.dom.appendChild(this.footer.dom);
31958     }
31959
31960     this.bg = this.el.createChild({
31961         tag: "div", cls:"x-dlg-bg",
31962         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31963     });
31964     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31965
31966
31967     if(this.autoScroll !== false && !this.autoTabs){
31968         this.body.setStyle("overflow", "auto");
31969     }
31970
31971     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31972
31973     if(this.closable !== false){
31974         this.el.addClass("x-dlg-closable");
31975         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31976         this.close.on("click", this.closeClick, this);
31977         this.close.addClassOnOver("x-dlg-close-over");
31978     }
31979     if(this.collapsible !== false){
31980         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31981         this.collapseBtn.on("click", this.collapseClick, this);
31982         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
31983         this.header.on("dblclick", this.collapseClick, this);
31984     }
31985     if(this.resizable !== false){
31986         this.el.addClass("x-dlg-resizable");
31987         this.resizer = new Roo.Resizable(el, {
31988             minWidth: this.minWidth || 80,
31989             minHeight:this.minHeight || 80,
31990             handles: this.resizeHandles || "all",
31991             pinned: true
31992         });
31993         this.resizer.on("beforeresize", this.beforeResize, this);
31994         this.resizer.on("resize", this.onResize, this);
31995     }
31996     if(this.draggable !== false){
31997         el.addClass("x-dlg-draggable");
31998         if (!this.proxyDrag) {
31999             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32000         }
32001         else {
32002             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32003         }
32004         dd.setHandleElId(this.header.id);
32005         dd.endDrag = this.endMove.createDelegate(this);
32006         dd.startDrag = this.startMove.createDelegate(this);
32007         dd.onDrag = this.onDrag.createDelegate(this);
32008         dd.scroll = false;
32009         this.dd = dd;
32010     }
32011     if(this.modal){
32012         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32013         this.mask.enableDisplayMode("block");
32014         this.mask.hide();
32015         this.el.addClass("x-dlg-modal");
32016     }
32017     if(this.shadow){
32018         this.shadow = new Roo.Shadow({
32019             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32020             offset : this.shadowOffset
32021         });
32022     }else{
32023         this.shadowOffset = 0;
32024     }
32025     if(Roo.useShims && this.shim !== false){
32026         this.shim = this.el.createShim();
32027         this.shim.hide = this.hideAction;
32028         this.shim.hide();
32029     }else{
32030         this.shim = false;
32031     }
32032     if(this.autoTabs){
32033         this.initTabs();
32034     }
32035     if (this.buttons) { 
32036         var bts= this.buttons;
32037         this.buttons = [];
32038         Roo.each(bts, function(b) {
32039             this.addButton(b);
32040         }, this);
32041     }
32042     
32043     
32044     this.addEvents({
32045         /**
32046          * @event keydown
32047          * Fires when a key is pressed
32048          * @param {Roo.BasicDialog} this
32049          * @param {Roo.EventObject} e
32050          */
32051         "keydown" : true,
32052         /**
32053          * @event move
32054          * Fires when this dialog is moved by the user.
32055          * @param {Roo.BasicDialog} this
32056          * @param {Number} x The new page X
32057          * @param {Number} y The new page Y
32058          */
32059         "move" : true,
32060         /**
32061          * @event resize
32062          * Fires when this dialog is resized by the user.
32063          * @param {Roo.BasicDialog} this
32064          * @param {Number} width The new width
32065          * @param {Number} height The new height
32066          */
32067         "resize" : true,
32068         /**
32069          * @event beforehide
32070          * Fires before this dialog is hidden.
32071          * @param {Roo.BasicDialog} this
32072          */
32073         "beforehide" : true,
32074         /**
32075          * @event hide
32076          * Fires when this dialog is hidden.
32077          * @param {Roo.BasicDialog} this
32078          */
32079         "hide" : true,
32080         /**
32081          * @event beforeshow
32082          * Fires before this dialog is shown.
32083          * @param {Roo.BasicDialog} this
32084          */
32085         "beforeshow" : true,
32086         /**
32087          * @event show
32088          * Fires when this dialog is shown.
32089          * @param {Roo.BasicDialog} this
32090          */
32091         "show" : true
32092     });
32093     el.on("keydown", this.onKeyDown, this);
32094     el.on("mousedown", this.toFront, this);
32095     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32096     this.el.hide();
32097     Roo.DialogManager.register(this);
32098     Roo.BasicDialog.superclass.constructor.call(this);
32099 };
32100
32101 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32102     shadowOffset: Roo.isIE ? 6 : 5,
32103     minHeight: 80,
32104     minWidth: 200,
32105     minButtonWidth: 75,
32106     defaultButton: null,
32107     buttonAlign: "right",
32108     tabTag: 'div',
32109     firstShow: true,
32110
32111     /**
32112      * Sets the dialog title text
32113      * @param {String} text The title text to display
32114      * @return {Roo.BasicDialog} this
32115      */
32116     setTitle : function(text){
32117         this.header.update(text);
32118         return this;
32119     },
32120
32121     // private
32122     closeClick : function(){
32123         this.hide();
32124     },
32125
32126     // private
32127     collapseClick : function(){
32128         this[this.collapsed ? "expand" : "collapse"]();
32129     },
32130
32131     /**
32132      * Collapses the dialog to its minimized state (only the title bar is visible).
32133      * Equivalent to the user clicking the collapse dialog button.
32134      */
32135     collapse : function(){
32136         if(!this.collapsed){
32137             this.collapsed = true;
32138             this.el.addClass("x-dlg-collapsed");
32139             this.restoreHeight = this.el.getHeight();
32140             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32141         }
32142     },
32143
32144     /**
32145      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32146      * clicking the expand dialog button.
32147      */
32148     expand : function(){
32149         if(this.collapsed){
32150             this.collapsed = false;
32151             this.el.removeClass("x-dlg-collapsed");
32152             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32153         }
32154     },
32155
32156     /**
32157      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32158      * @return {Roo.TabPanel} The tabs component
32159      */
32160     initTabs : function(){
32161         var tabs = this.getTabs();
32162         while(tabs.getTab(0)){
32163             tabs.removeTab(0);
32164         }
32165         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32166             var dom = el.dom;
32167             tabs.addTab(Roo.id(dom), dom.title);
32168             dom.title = "";
32169         });
32170         tabs.activate(0);
32171         return tabs;
32172     },
32173
32174     // private
32175     beforeResize : function(){
32176         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32177     },
32178
32179     // private
32180     onResize : function(){
32181         this.refreshSize();
32182         this.syncBodyHeight();
32183         this.adjustAssets();
32184         this.focus();
32185         this.fireEvent("resize", this, this.size.width, this.size.height);
32186     },
32187
32188     // private
32189     onKeyDown : function(e){
32190         if(this.isVisible()){
32191             this.fireEvent("keydown", this, e);
32192         }
32193     },
32194
32195     /**
32196      * Resizes the dialog.
32197      * @param {Number} width
32198      * @param {Number} height
32199      * @return {Roo.BasicDialog} this
32200      */
32201     resizeTo : function(width, height){
32202         this.el.setSize(width, height);
32203         this.size = {width: width, height: height};
32204         this.syncBodyHeight();
32205         if(this.fixedcenter){
32206             this.center();
32207         }
32208         if(this.isVisible()){
32209             this.constrainXY();
32210             this.adjustAssets();
32211         }
32212         this.fireEvent("resize", this, width, height);
32213         return this;
32214     },
32215
32216
32217     /**
32218      * Resizes the dialog to fit the specified content size.
32219      * @param {Number} width
32220      * @param {Number} height
32221      * @return {Roo.BasicDialog} this
32222      */
32223     setContentSize : function(w, h){
32224         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32225         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32226         //if(!this.el.isBorderBox()){
32227             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32228             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32229         //}
32230         if(this.tabs){
32231             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32232             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32233         }
32234         this.resizeTo(w, h);
32235         return this;
32236     },
32237
32238     /**
32239      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32240      * executed in response to a particular key being pressed while the dialog is active.
32241      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32242      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32243      * @param {Function} fn The function to call
32244      * @param {Object} scope (optional) The scope of the function
32245      * @return {Roo.BasicDialog} this
32246      */
32247     addKeyListener : function(key, fn, scope){
32248         var keyCode, shift, ctrl, alt;
32249         if(typeof key == "object" && !(key instanceof Array)){
32250             keyCode = key["key"];
32251             shift = key["shift"];
32252             ctrl = key["ctrl"];
32253             alt = key["alt"];
32254         }else{
32255             keyCode = key;
32256         }
32257         var handler = function(dlg, e){
32258             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32259                 var k = e.getKey();
32260                 if(keyCode instanceof Array){
32261                     for(var i = 0, len = keyCode.length; i < len; i++){
32262                         if(keyCode[i] == k){
32263                           fn.call(scope || window, dlg, k, e);
32264                           return;
32265                         }
32266                     }
32267                 }else{
32268                     if(k == keyCode){
32269                         fn.call(scope || window, dlg, k, e);
32270                     }
32271                 }
32272             }
32273         };
32274         this.on("keydown", handler);
32275         return this;
32276     },
32277
32278     /**
32279      * Returns the TabPanel component (creates it if it doesn't exist).
32280      * Note: If you wish to simply check for the existence of tabs without creating them,
32281      * check for a null 'tabs' property.
32282      * @return {Roo.TabPanel} The tabs component
32283      */
32284     getTabs : function(){
32285         if(!this.tabs){
32286             this.el.addClass("x-dlg-auto-tabs");
32287             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32288             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32289         }
32290         return this.tabs;
32291     },
32292
32293     /**
32294      * Adds a button to the footer section of the dialog.
32295      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32296      * object or a valid Roo.DomHelper element config
32297      * @param {Function} handler The function called when the button is clicked
32298      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32299      * @return {Roo.Button} The new button
32300      */
32301     addButton : function(config, handler, scope){
32302         var dh = Roo.DomHelper;
32303         if(!this.footer){
32304             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32305         }
32306         if(!this.btnContainer){
32307             var tb = this.footer.createChild({
32308
32309                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32310                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32311             }, null, true);
32312             this.btnContainer = tb.firstChild.firstChild.firstChild;
32313         }
32314         var bconfig = {
32315             handler: handler,
32316             scope: scope,
32317             minWidth: this.minButtonWidth,
32318             hideParent:true
32319         };
32320         if(typeof config == "string"){
32321             bconfig.text = config;
32322         }else{
32323             if(config.tag){
32324                 bconfig.dhconfig = config;
32325             }else{
32326                 Roo.apply(bconfig, config);
32327             }
32328         }
32329         var fc = false;
32330         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32331             bconfig.position = Math.max(0, bconfig.position);
32332             fc = this.btnContainer.childNodes[bconfig.position];
32333         }
32334          
32335         var btn = new Roo.Button(
32336             fc ? 
32337                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32338                 : this.btnContainer.appendChild(document.createElement("td")),
32339             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32340             bconfig
32341         );
32342         this.syncBodyHeight();
32343         if(!this.buttons){
32344             /**
32345              * Array of all the buttons that have been added to this dialog via addButton
32346              * @type Array
32347              */
32348             this.buttons = [];
32349         }
32350         this.buttons.push(btn);
32351         return btn;
32352     },
32353
32354     /**
32355      * Sets the default button to be focused when the dialog is displayed.
32356      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32357      * @return {Roo.BasicDialog} this
32358      */
32359     setDefaultButton : function(btn){
32360         this.defaultButton = btn;
32361         return this;
32362     },
32363
32364     // private
32365     getHeaderFooterHeight : function(safe){
32366         var height = 0;
32367         if(this.header){
32368            height += this.header.getHeight();
32369         }
32370         if(this.footer){
32371            var fm = this.footer.getMargins();
32372             height += (this.footer.getHeight()+fm.top+fm.bottom);
32373         }
32374         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32375         height += this.centerBg.getPadding("tb");
32376         return height;
32377     },
32378
32379     // private
32380     syncBodyHeight : function()
32381     {
32382         var bd = this.body, // the text
32383             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32384             bw = this.bwrap;
32385         var height = this.size.height - this.getHeaderFooterHeight(false);
32386         bd.setHeight(height-bd.getMargins("tb"));
32387         var hh = this.header.getHeight();
32388         var h = this.size.height-hh;
32389         cb.setHeight(h);
32390         
32391         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32392         bw.setHeight(h-cb.getPadding("tb"));
32393         
32394         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32395         bd.setWidth(bw.getWidth(true));
32396         if(this.tabs){
32397             this.tabs.syncHeight();
32398             if(Roo.isIE){
32399                 this.tabs.el.repaint();
32400             }
32401         }
32402     },
32403
32404     /**
32405      * Restores the previous state of the dialog if Roo.state is configured.
32406      * @return {Roo.BasicDialog} this
32407      */
32408     restoreState : function(){
32409         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32410         if(box && box.width){
32411             this.xy = [box.x, box.y];
32412             this.resizeTo(box.width, box.height);
32413         }
32414         return this;
32415     },
32416
32417     // private
32418     beforeShow : function(){
32419         this.expand();
32420         if(this.fixedcenter){
32421             this.xy = this.el.getCenterXY(true);
32422         }
32423         if(this.modal){
32424             Roo.get(document.body).addClass("x-body-masked");
32425             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32426             this.mask.show();
32427         }
32428         this.constrainXY();
32429     },
32430
32431     // private
32432     animShow : function(){
32433         var b = Roo.get(this.animateTarget).getBox();
32434         this.proxy.setSize(b.width, b.height);
32435         this.proxy.setLocation(b.x, b.y);
32436         this.proxy.show();
32437         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32438                     true, .35, this.showEl.createDelegate(this));
32439     },
32440
32441     /**
32442      * Shows the dialog.
32443      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32444      * @return {Roo.BasicDialog} this
32445      */
32446     show : function(animateTarget){
32447         if (this.fireEvent("beforeshow", this) === false){
32448             return;
32449         }
32450         if(this.syncHeightBeforeShow){
32451             this.syncBodyHeight();
32452         }else if(this.firstShow){
32453             this.firstShow = false;
32454             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32455         }
32456         this.animateTarget = animateTarget || this.animateTarget;
32457         if(!this.el.isVisible()){
32458             this.beforeShow();
32459             if(this.animateTarget && Roo.get(this.animateTarget)){
32460                 this.animShow();
32461             }else{
32462                 this.showEl();
32463             }
32464         }
32465         return this;
32466     },
32467
32468     // private
32469     showEl : function(){
32470         this.proxy.hide();
32471         this.el.setXY(this.xy);
32472         this.el.show();
32473         this.adjustAssets(true);
32474         this.toFront();
32475         this.focus();
32476         // IE peekaboo bug - fix found by Dave Fenwick
32477         if(Roo.isIE){
32478             this.el.repaint();
32479         }
32480         this.fireEvent("show", this);
32481     },
32482
32483     /**
32484      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32485      * dialog itself will receive focus.
32486      */
32487     focus : function(){
32488         if(this.defaultButton){
32489             this.defaultButton.focus();
32490         }else{
32491             this.focusEl.focus();
32492         }
32493     },
32494
32495     // private
32496     constrainXY : function(){
32497         if(this.constraintoviewport !== false){
32498             if(!this.viewSize){
32499                 if(this.container){
32500                     var s = this.container.getSize();
32501                     this.viewSize = [s.width, s.height];
32502                 }else{
32503                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32504                 }
32505             }
32506             var s = Roo.get(this.container||document).getScroll();
32507
32508             var x = this.xy[0], y = this.xy[1];
32509             var w = this.size.width, h = this.size.height;
32510             var vw = this.viewSize[0], vh = this.viewSize[1];
32511             // only move it if it needs it
32512             var moved = false;
32513             // first validate right/bottom
32514             if(x + w > vw+s.left){
32515                 x = vw - w;
32516                 moved = true;
32517             }
32518             if(y + h > vh+s.top){
32519                 y = vh - h;
32520                 moved = true;
32521             }
32522             // then make sure top/left isn't negative
32523             if(x < s.left){
32524                 x = s.left;
32525                 moved = true;
32526             }
32527             if(y < s.top){
32528                 y = s.top;
32529                 moved = true;
32530             }
32531             if(moved){
32532                 // cache xy
32533                 this.xy = [x, y];
32534                 if(this.isVisible()){
32535                     this.el.setLocation(x, y);
32536                     this.adjustAssets();
32537                 }
32538             }
32539         }
32540     },
32541
32542     // private
32543     onDrag : function(){
32544         if(!this.proxyDrag){
32545             this.xy = this.el.getXY();
32546             this.adjustAssets();
32547         }
32548     },
32549
32550     // private
32551     adjustAssets : function(doShow){
32552         var x = this.xy[0], y = this.xy[1];
32553         var w = this.size.width, h = this.size.height;
32554         if(doShow === true){
32555             if(this.shadow){
32556                 this.shadow.show(this.el);
32557             }
32558             if(this.shim){
32559                 this.shim.show();
32560             }
32561         }
32562         if(this.shadow && this.shadow.isVisible()){
32563             this.shadow.show(this.el);
32564         }
32565         if(this.shim && this.shim.isVisible()){
32566             this.shim.setBounds(x, y, w, h);
32567         }
32568     },
32569
32570     // private
32571     adjustViewport : function(w, h){
32572         if(!w || !h){
32573             w = Roo.lib.Dom.getViewWidth();
32574             h = Roo.lib.Dom.getViewHeight();
32575         }
32576         // cache the size
32577         this.viewSize = [w, h];
32578         if(this.modal && this.mask.isVisible()){
32579             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32580             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32581         }
32582         if(this.isVisible()){
32583             this.constrainXY();
32584         }
32585     },
32586
32587     /**
32588      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32589      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32590      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32591      */
32592     destroy : function(removeEl){
32593         if(this.isVisible()){
32594             this.animateTarget = null;
32595             this.hide();
32596         }
32597         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32598         if(this.tabs){
32599             this.tabs.destroy(removeEl);
32600         }
32601         Roo.destroy(
32602              this.shim,
32603              this.proxy,
32604              this.resizer,
32605              this.close,
32606              this.mask
32607         );
32608         if(this.dd){
32609             this.dd.unreg();
32610         }
32611         if(this.buttons){
32612            for(var i = 0, len = this.buttons.length; i < len; i++){
32613                this.buttons[i].destroy();
32614            }
32615         }
32616         this.el.removeAllListeners();
32617         if(removeEl === true){
32618             this.el.update("");
32619             this.el.remove();
32620         }
32621         Roo.DialogManager.unregister(this);
32622     },
32623
32624     // private
32625     startMove : function(){
32626         if(this.proxyDrag){
32627             this.proxy.show();
32628         }
32629         if(this.constraintoviewport !== false){
32630             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32631         }
32632     },
32633
32634     // private
32635     endMove : function(){
32636         if(!this.proxyDrag){
32637             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32638         }else{
32639             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32640             this.proxy.hide();
32641         }
32642         this.refreshSize();
32643         this.adjustAssets();
32644         this.focus();
32645         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32646     },
32647
32648     /**
32649      * Brings this dialog to the front of any other visible dialogs
32650      * @return {Roo.BasicDialog} this
32651      */
32652     toFront : function(){
32653         Roo.DialogManager.bringToFront(this);
32654         return this;
32655     },
32656
32657     /**
32658      * Sends this dialog to the back (under) of any other visible dialogs
32659      * @return {Roo.BasicDialog} this
32660      */
32661     toBack : function(){
32662         Roo.DialogManager.sendToBack(this);
32663         return this;
32664     },
32665
32666     /**
32667      * Centers this dialog in the viewport
32668      * @return {Roo.BasicDialog} this
32669      */
32670     center : function(){
32671         var xy = this.el.getCenterXY(true);
32672         this.moveTo(xy[0], xy[1]);
32673         return this;
32674     },
32675
32676     /**
32677      * Moves the dialog's top-left corner to the specified point
32678      * @param {Number} x
32679      * @param {Number} y
32680      * @return {Roo.BasicDialog} this
32681      */
32682     moveTo : function(x, y){
32683         this.xy = [x,y];
32684         if(this.isVisible()){
32685             this.el.setXY(this.xy);
32686             this.adjustAssets();
32687         }
32688         return this;
32689     },
32690
32691     /**
32692      * Aligns the dialog to the specified element
32693      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32694      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32695      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32696      * @return {Roo.BasicDialog} this
32697      */
32698     alignTo : function(element, position, offsets){
32699         this.xy = this.el.getAlignToXY(element, position, offsets);
32700         if(this.isVisible()){
32701             this.el.setXY(this.xy);
32702             this.adjustAssets();
32703         }
32704         return this;
32705     },
32706
32707     /**
32708      * Anchors an element to another element and realigns it when the window is resized.
32709      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32710      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32711      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32712      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32713      * is a number, it is used as the buffer delay (defaults to 50ms).
32714      * @return {Roo.BasicDialog} this
32715      */
32716     anchorTo : function(el, alignment, offsets, monitorScroll){
32717         var action = function(){
32718             this.alignTo(el, alignment, offsets);
32719         };
32720         Roo.EventManager.onWindowResize(action, this);
32721         var tm = typeof monitorScroll;
32722         if(tm != 'undefined'){
32723             Roo.EventManager.on(window, 'scroll', action, this,
32724                 {buffer: tm == 'number' ? monitorScroll : 50});
32725         }
32726         action.call(this);
32727         return this;
32728     },
32729
32730     /**
32731      * Returns true if the dialog is visible
32732      * @return {Boolean}
32733      */
32734     isVisible : function(){
32735         return this.el.isVisible();
32736     },
32737
32738     // private
32739     animHide : function(callback){
32740         var b = Roo.get(this.animateTarget).getBox();
32741         this.proxy.show();
32742         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32743         this.el.hide();
32744         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32745                     this.hideEl.createDelegate(this, [callback]));
32746     },
32747
32748     /**
32749      * Hides the dialog.
32750      * @param {Function} callback (optional) Function to call when the dialog is hidden
32751      * @return {Roo.BasicDialog} this
32752      */
32753     hide : function(callback){
32754         if (this.fireEvent("beforehide", this) === false){
32755             return;
32756         }
32757         if(this.shadow){
32758             this.shadow.hide();
32759         }
32760         if(this.shim) {
32761           this.shim.hide();
32762         }
32763         // sometimes animateTarget seems to get set.. causing problems...
32764         // this just double checks..
32765         if(this.animateTarget && Roo.get(this.animateTarget)) {
32766            this.animHide(callback);
32767         }else{
32768             this.el.hide();
32769             this.hideEl(callback);
32770         }
32771         return this;
32772     },
32773
32774     // private
32775     hideEl : function(callback){
32776         this.proxy.hide();
32777         if(this.modal){
32778             this.mask.hide();
32779             Roo.get(document.body).removeClass("x-body-masked");
32780         }
32781         this.fireEvent("hide", this);
32782         if(typeof callback == "function"){
32783             callback();
32784         }
32785     },
32786
32787     // private
32788     hideAction : function(){
32789         this.setLeft("-10000px");
32790         this.setTop("-10000px");
32791         this.setStyle("visibility", "hidden");
32792     },
32793
32794     // private
32795     refreshSize : function(){
32796         this.size = this.el.getSize();
32797         this.xy = this.el.getXY();
32798         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32799     },
32800
32801     // private
32802     // z-index is managed by the DialogManager and may be overwritten at any time
32803     setZIndex : function(index){
32804         if(this.modal){
32805             this.mask.setStyle("z-index", index);
32806         }
32807         if(this.shim){
32808             this.shim.setStyle("z-index", ++index);
32809         }
32810         if(this.shadow){
32811             this.shadow.setZIndex(++index);
32812         }
32813         this.el.setStyle("z-index", ++index);
32814         if(this.proxy){
32815             this.proxy.setStyle("z-index", ++index);
32816         }
32817         if(this.resizer){
32818             this.resizer.proxy.setStyle("z-index", ++index);
32819         }
32820
32821         this.lastZIndex = index;
32822     },
32823
32824     /**
32825      * Returns the element for this dialog
32826      * @return {Roo.Element} The underlying dialog Element
32827      */
32828     getEl : function(){
32829         return this.el;
32830     }
32831 });
32832
32833 /**
32834  * @class Roo.DialogManager
32835  * Provides global access to BasicDialogs that have been created and
32836  * support for z-indexing (layering) multiple open dialogs.
32837  */
32838 Roo.DialogManager = function(){
32839     var list = {};
32840     var accessList = [];
32841     var front = null;
32842
32843     // private
32844     var sortDialogs = function(d1, d2){
32845         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32846     };
32847
32848     // private
32849     var orderDialogs = function(){
32850         accessList.sort(sortDialogs);
32851         var seed = Roo.DialogManager.zseed;
32852         for(var i = 0, len = accessList.length; i < len; i++){
32853             var dlg = accessList[i];
32854             if(dlg){
32855                 dlg.setZIndex(seed + (i*10));
32856             }
32857         }
32858     };
32859
32860     return {
32861         /**
32862          * The starting z-index for BasicDialogs (defaults to 9000)
32863          * @type Number The z-index value
32864          */
32865         zseed : 9000,
32866
32867         // private
32868         register : function(dlg){
32869             list[dlg.id] = dlg;
32870             accessList.push(dlg);
32871         },
32872
32873         // private
32874         unregister : function(dlg){
32875             delete list[dlg.id];
32876             var i=0;
32877             var len=0;
32878             if(!accessList.indexOf){
32879                 for(  i = 0, len = accessList.length; i < len; i++){
32880                     if(accessList[i] == dlg){
32881                         accessList.splice(i, 1);
32882                         return;
32883                     }
32884                 }
32885             }else{
32886                  i = accessList.indexOf(dlg);
32887                 if(i != -1){
32888                     accessList.splice(i, 1);
32889                 }
32890             }
32891         },
32892
32893         /**
32894          * Gets a registered dialog by id
32895          * @param {String/Object} id The id of the dialog or a dialog
32896          * @return {Roo.BasicDialog} this
32897          */
32898         get : function(id){
32899             return typeof id == "object" ? id : list[id];
32900         },
32901
32902         /**
32903          * Brings the specified dialog to the front
32904          * @param {String/Object} dlg The id of the dialog or a dialog
32905          * @return {Roo.BasicDialog} this
32906          */
32907         bringToFront : function(dlg){
32908             dlg = this.get(dlg);
32909             if(dlg != front){
32910                 front = dlg;
32911                 dlg._lastAccess = new Date().getTime();
32912                 orderDialogs();
32913             }
32914             return dlg;
32915         },
32916
32917         /**
32918          * Sends the specified dialog to the back
32919          * @param {String/Object} dlg The id of the dialog or a dialog
32920          * @return {Roo.BasicDialog} this
32921          */
32922         sendToBack : function(dlg){
32923             dlg = this.get(dlg);
32924             dlg._lastAccess = -(new Date().getTime());
32925             orderDialogs();
32926             return dlg;
32927         },
32928
32929         /**
32930          * Hides all dialogs
32931          */
32932         hideAll : function(){
32933             for(var id in list){
32934                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32935                     list[id].hide();
32936                 }
32937             }
32938         }
32939     };
32940 }();
32941
32942 /**
32943  * @class Roo.LayoutDialog
32944  * @extends Roo.BasicDialog
32945  * Dialog which provides adjustments for working with a layout in a Dialog.
32946  * Add your necessary layout config options to the dialog's config.<br>
32947  * Example usage (including a nested layout):
32948  * <pre><code>
32949 if(!dialog){
32950     dialog = new Roo.LayoutDialog("download-dlg", {
32951         modal: true,
32952         width:600,
32953         height:450,
32954         shadow:true,
32955         minWidth:500,
32956         minHeight:350,
32957         autoTabs:true,
32958         proxyDrag:true,
32959         // layout config merges with the dialog config
32960         center:{
32961             tabPosition: "top",
32962             alwaysShowTabs: true
32963         }
32964     });
32965     dialog.addKeyListener(27, dialog.hide, dialog);
32966     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32967     dialog.addButton("Build It!", this.getDownload, this);
32968
32969     // we can even add nested layouts
32970     var innerLayout = new Roo.BorderLayout("dl-inner", {
32971         east: {
32972             initialSize: 200,
32973             autoScroll:true,
32974             split:true
32975         },
32976         center: {
32977             autoScroll:true
32978         }
32979     });
32980     innerLayout.beginUpdate();
32981     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
32982     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
32983     innerLayout.endUpdate(true);
32984
32985     var layout = dialog.getLayout();
32986     layout.beginUpdate();
32987     layout.add("center", new Roo.ContentPanel("standard-panel",
32988                         {title: "Download the Source", fitToFrame:true}));
32989     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
32990                {title: "Build your own roo.js"}));
32991     layout.getRegion("center").showPanel(sp);
32992     layout.endUpdate();
32993 }
32994 </code></pre>
32995     * @constructor
32996     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
32997     * @param {Object} config configuration options
32998   */
32999 Roo.LayoutDialog = function(el, cfg){
33000     
33001     var config=  cfg;
33002     if (typeof(cfg) == 'undefined') {
33003         config = Roo.apply({}, el);
33004         // not sure why we use documentElement here.. - it should always be body.
33005         // IE7 borks horribly if we use documentElement.
33006         // webkit also does not like documentElement - it creates a body element...
33007         el = Roo.get( document.body || document.documentElement ).createChild();
33008         //config.autoCreate = true;
33009     }
33010     
33011     
33012     config.autoTabs = false;
33013     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33014     this.body.setStyle({overflow:"hidden", position:"relative"});
33015     this.layout = new Roo.BorderLayout(this.body.dom, config);
33016     this.layout.monitorWindowResize = false;
33017     this.el.addClass("x-dlg-auto-layout");
33018     // fix case when center region overwrites center function
33019     this.center = Roo.BasicDialog.prototype.center;
33020     this.on("show", this.layout.layout, this.layout, true);
33021     if (config.items) {
33022         var xitems = config.items;
33023         delete config.items;
33024         Roo.each(xitems, this.addxtype, this);
33025     }
33026     
33027     
33028 };
33029 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33030     /**
33031      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33032      * @deprecated
33033      */
33034     endUpdate : function(){
33035         this.layout.endUpdate();
33036     },
33037
33038     /**
33039      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33040      *  @deprecated
33041      */
33042     beginUpdate : function(){
33043         this.layout.beginUpdate();
33044     },
33045
33046     /**
33047      * Get the BorderLayout for this dialog
33048      * @return {Roo.BorderLayout}
33049      */
33050     getLayout : function(){
33051         return this.layout;
33052     },
33053
33054     showEl : function(){
33055         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33056         if(Roo.isIE7){
33057             this.layout.layout();
33058         }
33059     },
33060
33061     // private
33062     // Use the syncHeightBeforeShow config option to control this automatically
33063     syncBodyHeight : function(){
33064         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33065         if(this.layout){this.layout.layout();}
33066     },
33067     
33068       /**
33069      * Add an xtype element (actually adds to the layout.)
33070      * @return {Object} xdata xtype object data.
33071      */
33072     
33073     addxtype : function(c) {
33074         return this.layout.addxtype(c);
33075     }
33076 });/*
33077  * Based on:
33078  * Ext JS Library 1.1.1
33079  * Copyright(c) 2006-2007, Ext JS, LLC.
33080  *
33081  * Originally Released Under LGPL - original licence link has changed is not relivant.
33082  *
33083  * Fork - LGPL
33084  * <script type="text/javascript">
33085  */
33086  
33087 /**
33088  * @class Roo.MessageBox
33089  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33090  * Example usage:
33091  *<pre><code>
33092 // Basic alert:
33093 Roo.Msg.alert('Status', 'Changes saved successfully.');
33094
33095 // Prompt for user data:
33096 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33097     if (btn == 'ok'){
33098         // process text value...
33099     }
33100 });
33101
33102 // Show a dialog using config options:
33103 Roo.Msg.show({
33104    title:'Save Changes?',
33105    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33106    buttons: Roo.Msg.YESNOCANCEL,
33107    fn: processResult,
33108    animEl: 'elId'
33109 });
33110 </code></pre>
33111  * @singleton
33112  */
33113 Roo.MessageBox = function(){
33114     var dlg, opt, mask, waitTimer;
33115     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33116     var buttons, activeTextEl, bwidth;
33117
33118     // private
33119     var handleButton = function(button){
33120         dlg.hide();
33121         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33122     };
33123
33124     // private
33125     var handleHide = function(){
33126         if(opt && opt.cls){
33127             dlg.el.removeClass(opt.cls);
33128         }
33129         if(waitTimer){
33130             Roo.TaskMgr.stop(waitTimer);
33131             waitTimer = null;
33132         }
33133     };
33134
33135     // private
33136     var updateButtons = function(b){
33137         var width = 0;
33138         if(!b){
33139             buttons["ok"].hide();
33140             buttons["cancel"].hide();
33141             buttons["yes"].hide();
33142             buttons["no"].hide();
33143             dlg.footer.dom.style.display = 'none';
33144             return width;
33145         }
33146         dlg.footer.dom.style.display = '';
33147         for(var k in buttons){
33148             if(typeof buttons[k] != "function"){
33149                 if(b[k]){
33150                     buttons[k].show();
33151                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33152                     width += buttons[k].el.getWidth()+15;
33153                 }else{
33154                     buttons[k].hide();
33155                 }
33156             }
33157         }
33158         return width;
33159     };
33160
33161     // private
33162     var handleEsc = function(d, k, e){
33163         if(opt && opt.closable !== false){
33164             dlg.hide();
33165         }
33166         if(e){
33167             e.stopEvent();
33168         }
33169     };
33170
33171     return {
33172         /**
33173          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33174          * @return {Roo.BasicDialog} The BasicDialog element
33175          */
33176         getDialog : function(){
33177            if(!dlg){
33178                 dlg = new Roo.BasicDialog("x-msg-box", {
33179                     autoCreate : true,
33180                     shadow: true,
33181                     draggable: true,
33182                     resizable:false,
33183                     constraintoviewport:false,
33184                     fixedcenter:true,
33185                     collapsible : false,
33186                     shim:true,
33187                     modal: true,
33188                     width:400, height:100,
33189                     buttonAlign:"center",
33190                     closeClick : function(){
33191                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33192                             handleButton("no");
33193                         }else{
33194                             handleButton("cancel");
33195                         }
33196                     }
33197                 });
33198                 dlg.on("hide", handleHide);
33199                 mask = dlg.mask;
33200                 dlg.addKeyListener(27, handleEsc);
33201                 buttons = {};
33202                 var bt = this.buttonText;
33203                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33204                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33205                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33206                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33207                 bodyEl = dlg.body.createChild({
33208
33209                     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>'
33210                 });
33211                 msgEl = bodyEl.dom.firstChild;
33212                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33213                 textboxEl.enableDisplayMode();
33214                 textboxEl.addKeyListener([10,13], function(){
33215                     if(dlg.isVisible() && opt && opt.buttons){
33216                         if(opt.buttons.ok){
33217                             handleButton("ok");
33218                         }else if(opt.buttons.yes){
33219                             handleButton("yes");
33220                         }
33221                     }
33222                 });
33223                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33224                 textareaEl.enableDisplayMode();
33225                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33226                 progressEl.enableDisplayMode();
33227                 var pf = progressEl.dom.firstChild;
33228                 if (pf) {
33229                     pp = Roo.get(pf.firstChild);
33230                     pp.setHeight(pf.offsetHeight);
33231                 }
33232                 
33233             }
33234             return dlg;
33235         },
33236
33237         /**
33238          * Updates the message box body text
33239          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33240          * the XHTML-compliant non-breaking space character '&amp;#160;')
33241          * @return {Roo.MessageBox} This message box
33242          */
33243         updateText : function(text){
33244             if(!dlg.isVisible() && !opt.width){
33245                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33246             }
33247             msgEl.innerHTML = text || '&#160;';
33248       
33249             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33250             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33251             var w = Math.max(
33252                     Math.min(opt.width || cw , this.maxWidth), 
33253                     Math.max(opt.minWidth || this.minWidth, bwidth)
33254             );
33255             if(opt.prompt){
33256                 activeTextEl.setWidth(w);
33257             }
33258             if(dlg.isVisible()){
33259                 dlg.fixedcenter = false;
33260             }
33261             // to big, make it scroll. = But as usual stupid IE does not support
33262             // !important..
33263             
33264             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33265                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33266                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33267             } else {
33268                 bodyEl.dom.style.height = '';
33269                 bodyEl.dom.style.overflowY = '';
33270             }
33271             if (cw > w) {
33272                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33273             } else {
33274                 bodyEl.dom.style.overflowX = '';
33275             }
33276             
33277             dlg.setContentSize(w, bodyEl.getHeight());
33278             if(dlg.isVisible()){
33279                 dlg.fixedcenter = true;
33280             }
33281             return this;
33282         },
33283
33284         /**
33285          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33286          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33287          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33288          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33289          * @return {Roo.MessageBox} This message box
33290          */
33291         updateProgress : function(value, text){
33292             if(text){
33293                 this.updateText(text);
33294             }
33295             if (pp) { // weird bug on my firefox - for some reason this is not defined
33296                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33297             }
33298             return this;
33299         },        
33300
33301         /**
33302          * Returns true if the message box is currently displayed
33303          * @return {Boolean} True if the message box is visible, else false
33304          */
33305         isVisible : function(){
33306             return dlg && dlg.isVisible();  
33307         },
33308
33309         /**
33310          * Hides the message box if it is displayed
33311          */
33312         hide : function(){
33313             if(this.isVisible()){
33314                 dlg.hide();
33315             }  
33316         },
33317
33318         /**
33319          * Displays a new message box, or reinitializes an existing message box, based on the config options
33320          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33321          * The following config object properties are supported:
33322          * <pre>
33323 Property    Type             Description
33324 ----------  ---------------  ------------------------------------------------------------------------------------
33325 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33326                                    closes (defaults to undefined)
33327 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33328                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33329 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33330                                    progress and wait dialogs will ignore this property and always hide the
33331                                    close button as they can only be closed programmatically.
33332 cls               String           A custom CSS class to apply to the message box element
33333 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33334                                    displayed (defaults to 75)
33335 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33336                                    function will be btn (the name of the button that was clicked, if applicable,
33337                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33338                                    Progress and wait dialogs will ignore this option since they do not respond to
33339                                    user actions and can only be closed programmatically, so any required function
33340                                    should be called by the same code after it closes the dialog.
33341 icon              String           A CSS class that provides a background image to be used as an icon for
33342                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33343 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33344 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33345 modal             Boolean          False to allow user interaction with the page while the message box is
33346                                    displayed (defaults to true)
33347 msg               String           A string that will replace the existing message box body text (defaults
33348                                    to the XHTML-compliant non-breaking space character '&#160;')
33349 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33350 progress          Boolean          True to display a progress bar (defaults to false)
33351 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33352 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33353 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33354 title             String           The title text
33355 value             String           The string value to set into the active textbox element if displayed
33356 wait              Boolean          True to display a progress bar (defaults to false)
33357 width             Number           The width of the dialog in pixels
33358 </pre>
33359          *
33360          * Example usage:
33361          * <pre><code>
33362 Roo.Msg.show({
33363    title: 'Address',
33364    msg: 'Please enter your address:',
33365    width: 300,
33366    buttons: Roo.MessageBox.OKCANCEL,
33367    multiline: true,
33368    fn: saveAddress,
33369    animEl: 'addAddressBtn'
33370 });
33371 </code></pre>
33372          * @param {Object} config Configuration options
33373          * @return {Roo.MessageBox} This message box
33374          */
33375         show : function(options)
33376         {
33377             
33378             // this causes nightmares if you show one dialog after another
33379             // especially on callbacks..
33380              
33381             if(this.isVisible()){
33382                 
33383                 this.hide();
33384                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33385                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33386                 Roo.log("New Dialog Message:" +  options.msg )
33387                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33388                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33389                 
33390             }
33391             var d = this.getDialog();
33392             opt = options;
33393             d.setTitle(opt.title || "&#160;");
33394             d.close.setDisplayed(opt.closable !== false);
33395             activeTextEl = textboxEl;
33396             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33397             if(opt.prompt){
33398                 if(opt.multiline){
33399                     textboxEl.hide();
33400                     textareaEl.show();
33401                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33402                         opt.multiline : this.defaultTextHeight);
33403                     activeTextEl = textareaEl;
33404                 }else{
33405                     textboxEl.show();
33406                     textareaEl.hide();
33407                 }
33408             }else{
33409                 textboxEl.hide();
33410                 textareaEl.hide();
33411             }
33412             progressEl.setDisplayed(opt.progress === true);
33413             this.updateProgress(0);
33414             activeTextEl.dom.value = opt.value || "";
33415             if(opt.prompt){
33416                 dlg.setDefaultButton(activeTextEl);
33417             }else{
33418                 var bs = opt.buttons;
33419                 var db = null;
33420                 if(bs && bs.ok){
33421                     db = buttons["ok"];
33422                 }else if(bs && bs.yes){
33423                     db = buttons["yes"];
33424                 }
33425                 dlg.setDefaultButton(db);
33426             }
33427             bwidth = updateButtons(opt.buttons);
33428             this.updateText(opt.msg);
33429             if(opt.cls){
33430                 d.el.addClass(opt.cls);
33431             }
33432             d.proxyDrag = opt.proxyDrag === true;
33433             d.modal = opt.modal !== false;
33434             d.mask = opt.modal !== false ? mask : false;
33435             if(!d.isVisible()){
33436                 // force it to the end of the z-index stack so it gets a cursor in FF
33437                 document.body.appendChild(dlg.el.dom);
33438                 d.animateTarget = null;
33439                 d.show(options.animEl);
33440             }
33441             return this;
33442         },
33443
33444         /**
33445          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33446          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33447          * and closing the message box when the process is complete.
33448          * @param {String} title The title bar text
33449          * @param {String} msg The message box body text
33450          * @return {Roo.MessageBox} This message box
33451          */
33452         progress : function(title, msg){
33453             this.show({
33454                 title : title,
33455                 msg : msg,
33456                 buttons: false,
33457                 progress:true,
33458                 closable:false,
33459                 minWidth: this.minProgressWidth,
33460                 modal : true
33461             });
33462             return this;
33463         },
33464
33465         /**
33466          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33467          * If a callback function is passed it will be called after the user clicks the button, and the
33468          * id of the button that was clicked will be passed as the only parameter to the callback
33469          * (could also be the top-right close button).
33470          * @param {String} title The title bar text
33471          * @param {String} msg The message box body text
33472          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33473          * @param {Object} scope (optional) The scope of the callback function
33474          * @return {Roo.MessageBox} This message box
33475          */
33476         alert : function(title, msg, fn, scope){
33477             this.show({
33478                 title : title,
33479                 msg : msg,
33480                 buttons: this.OK,
33481                 fn: fn,
33482                 scope : scope,
33483                 modal : true
33484             });
33485             return this;
33486         },
33487
33488         /**
33489          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33490          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33491          * You are responsible for closing the message box when the process is complete.
33492          * @param {String} msg The message box body text
33493          * @param {String} title (optional) The title bar text
33494          * @return {Roo.MessageBox} This message box
33495          */
33496         wait : function(msg, title){
33497             this.show({
33498                 title : title,
33499                 msg : msg,
33500                 buttons: false,
33501                 closable:false,
33502                 progress:true,
33503                 modal:true,
33504                 width:300,
33505                 wait:true
33506             });
33507             waitTimer = Roo.TaskMgr.start({
33508                 run: function(i){
33509                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33510                 },
33511                 interval: 1000
33512             });
33513             return this;
33514         },
33515
33516         /**
33517          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33518          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33519          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33520          * @param {String} title The title bar text
33521          * @param {String} msg The message box body text
33522          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33523          * @param {Object} scope (optional) The scope of the callback function
33524          * @return {Roo.MessageBox} This message box
33525          */
33526         confirm : function(title, msg, fn, scope){
33527             this.show({
33528                 title : title,
33529                 msg : msg,
33530                 buttons: this.YESNO,
33531                 fn: fn,
33532                 scope : scope,
33533                 modal : true
33534             });
33535             return this;
33536         },
33537
33538         /**
33539          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33540          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33541          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33542          * (could also be the top-right close button) and the text that was entered will be passed as the two
33543          * parameters to the callback.
33544          * @param {String} title The title bar text
33545          * @param {String} msg The message box body text
33546          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33547          * @param {Object} scope (optional) The scope of the callback function
33548          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33549          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33550          * @return {Roo.MessageBox} This message box
33551          */
33552         prompt : function(title, msg, fn, scope, multiline){
33553             this.show({
33554                 title : title,
33555                 msg : msg,
33556                 buttons: this.OKCANCEL,
33557                 fn: fn,
33558                 minWidth:250,
33559                 scope : scope,
33560                 prompt:true,
33561                 multiline: multiline,
33562                 modal : true
33563             });
33564             return this;
33565         },
33566
33567         /**
33568          * Button config that displays a single OK button
33569          * @type Object
33570          */
33571         OK : {ok:true},
33572         /**
33573          * Button config that displays Yes and No buttons
33574          * @type Object
33575          */
33576         YESNO : {yes:true, no:true},
33577         /**
33578          * Button config that displays OK and Cancel buttons
33579          * @type Object
33580          */
33581         OKCANCEL : {ok:true, cancel:true},
33582         /**
33583          * Button config that displays Yes, No and Cancel buttons
33584          * @type Object
33585          */
33586         YESNOCANCEL : {yes:true, no:true, cancel:true},
33587
33588         /**
33589          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33590          * @type Number
33591          */
33592         defaultTextHeight : 75,
33593         /**
33594          * The maximum width in pixels of the message box (defaults to 600)
33595          * @type Number
33596          */
33597         maxWidth : 600,
33598         /**
33599          * The minimum width in pixels of the message box (defaults to 100)
33600          * @type Number
33601          */
33602         minWidth : 100,
33603         /**
33604          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33605          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33606          * @type Number
33607          */
33608         minProgressWidth : 250,
33609         /**
33610          * An object containing the default button text strings that can be overriden for localized language support.
33611          * Supported properties are: ok, cancel, yes and no.
33612          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33613          * @type Object
33614          */
33615         buttonText : {
33616             ok : "OK",
33617             cancel : "Cancel",
33618             yes : "Yes",
33619             no : "No"
33620         }
33621     };
33622 }();
33623
33624 /**
33625  * Shorthand for {@link Roo.MessageBox}
33626  */
33627 Roo.Msg = Roo.MessageBox;/*
33628  * Based on:
33629  * Ext JS Library 1.1.1
33630  * Copyright(c) 2006-2007, Ext JS, LLC.
33631  *
33632  * Originally Released Under LGPL - original licence link has changed is not relivant.
33633  *
33634  * Fork - LGPL
33635  * <script type="text/javascript">
33636  */
33637 /**
33638  * @class Roo.QuickTips
33639  * Provides attractive and customizable tooltips for any element.
33640  * @singleton
33641  */
33642 Roo.QuickTips = function(){
33643     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33644     var ce, bd, xy, dd;
33645     var visible = false, disabled = true, inited = false;
33646     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33647     
33648     var onOver = function(e){
33649         if(disabled){
33650             return;
33651         }
33652         var t = e.getTarget();
33653         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33654             return;
33655         }
33656         if(ce && t == ce.el){
33657             clearTimeout(hideProc);
33658             return;
33659         }
33660         if(t && tagEls[t.id]){
33661             tagEls[t.id].el = t;
33662             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33663             return;
33664         }
33665         var ttp, et = Roo.fly(t);
33666         var ns = cfg.namespace;
33667         if(tm.interceptTitles && t.title){
33668             ttp = t.title;
33669             t.qtip = ttp;
33670             t.removeAttribute("title");
33671             e.preventDefault();
33672         }else{
33673             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33674         }
33675         if(ttp){
33676             showProc = show.defer(tm.showDelay, tm, [{
33677                 el: t, 
33678                 text: ttp.replace(/\\n/g,'<br/>'),
33679                 width: et.getAttributeNS(ns, cfg.width),
33680                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33681                 title: et.getAttributeNS(ns, cfg.title),
33682                     cls: et.getAttributeNS(ns, cfg.cls)
33683             }]);
33684         }
33685     };
33686     
33687     var onOut = function(e){
33688         clearTimeout(showProc);
33689         var t = e.getTarget();
33690         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33691             hideProc = setTimeout(hide, tm.hideDelay);
33692         }
33693     };
33694     
33695     var onMove = function(e){
33696         if(disabled){
33697             return;
33698         }
33699         xy = e.getXY();
33700         xy[1] += 18;
33701         if(tm.trackMouse && ce){
33702             el.setXY(xy);
33703         }
33704     };
33705     
33706     var onDown = function(e){
33707         clearTimeout(showProc);
33708         clearTimeout(hideProc);
33709         if(!e.within(el)){
33710             if(tm.hideOnClick){
33711                 hide();
33712                 tm.disable();
33713                 tm.enable.defer(100, tm);
33714             }
33715         }
33716     };
33717     
33718     var getPad = function(){
33719         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33720     };
33721
33722     var show = function(o){
33723         if(disabled){
33724             return;
33725         }
33726         clearTimeout(dismissProc);
33727         ce = o;
33728         if(removeCls){ // in case manually hidden
33729             el.removeClass(removeCls);
33730             removeCls = null;
33731         }
33732         if(ce.cls){
33733             el.addClass(ce.cls);
33734             removeCls = ce.cls;
33735         }
33736         if(ce.title){
33737             tipTitle.update(ce.title);
33738             tipTitle.show();
33739         }else{
33740             tipTitle.update('');
33741             tipTitle.hide();
33742         }
33743         el.dom.style.width  = tm.maxWidth+'px';
33744         //tipBody.dom.style.width = '';
33745         tipBodyText.update(o.text);
33746         var p = getPad(), w = ce.width;
33747         if(!w){
33748             var td = tipBodyText.dom;
33749             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33750             if(aw > tm.maxWidth){
33751                 w = tm.maxWidth;
33752             }else if(aw < tm.minWidth){
33753                 w = tm.minWidth;
33754             }else{
33755                 w = aw;
33756             }
33757         }
33758         //tipBody.setWidth(w);
33759         el.setWidth(parseInt(w, 10) + p);
33760         if(ce.autoHide === false){
33761             close.setDisplayed(true);
33762             if(dd){
33763                 dd.unlock();
33764             }
33765         }else{
33766             close.setDisplayed(false);
33767             if(dd){
33768                 dd.lock();
33769             }
33770         }
33771         if(xy){
33772             el.avoidY = xy[1]-18;
33773             el.setXY(xy);
33774         }
33775         if(tm.animate){
33776             el.setOpacity(.1);
33777             el.setStyle("visibility", "visible");
33778             el.fadeIn({callback: afterShow});
33779         }else{
33780             afterShow();
33781         }
33782     };
33783     
33784     var afterShow = function(){
33785         if(ce){
33786             el.show();
33787             esc.enable();
33788             if(tm.autoDismiss && ce.autoHide !== false){
33789                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33790             }
33791         }
33792     };
33793     
33794     var hide = function(noanim){
33795         clearTimeout(dismissProc);
33796         clearTimeout(hideProc);
33797         ce = null;
33798         if(el.isVisible()){
33799             esc.disable();
33800             if(noanim !== true && tm.animate){
33801                 el.fadeOut({callback: afterHide});
33802             }else{
33803                 afterHide();
33804             } 
33805         }
33806     };
33807     
33808     var afterHide = function(){
33809         el.hide();
33810         if(removeCls){
33811             el.removeClass(removeCls);
33812             removeCls = null;
33813         }
33814     };
33815     
33816     return {
33817         /**
33818         * @cfg {Number} minWidth
33819         * The minimum width of the quick tip (defaults to 40)
33820         */
33821        minWidth : 40,
33822         /**
33823         * @cfg {Number} maxWidth
33824         * The maximum width of the quick tip (defaults to 300)
33825         */
33826        maxWidth : 300,
33827         /**
33828         * @cfg {Boolean} interceptTitles
33829         * True to automatically use the element's DOM title value if available (defaults to false)
33830         */
33831        interceptTitles : false,
33832         /**
33833         * @cfg {Boolean} trackMouse
33834         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33835         */
33836        trackMouse : false,
33837         /**
33838         * @cfg {Boolean} hideOnClick
33839         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33840         */
33841        hideOnClick : true,
33842         /**
33843         * @cfg {Number} showDelay
33844         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33845         */
33846        showDelay : 500,
33847         /**
33848         * @cfg {Number} hideDelay
33849         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33850         */
33851        hideDelay : 200,
33852         /**
33853         * @cfg {Boolean} autoHide
33854         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33855         * Used in conjunction with hideDelay.
33856         */
33857        autoHide : true,
33858         /**
33859         * @cfg {Boolean}
33860         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33861         * (defaults to true).  Used in conjunction with autoDismissDelay.
33862         */
33863        autoDismiss : true,
33864         /**
33865         * @cfg {Number}
33866         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33867         */
33868        autoDismissDelay : 5000,
33869        /**
33870         * @cfg {Boolean} animate
33871         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33872         */
33873        animate : false,
33874
33875        /**
33876         * @cfg {String} title
33877         * Title text to display (defaults to '').  This can be any valid HTML markup.
33878         */
33879         title: '',
33880        /**
33881         * @cfg {String} text
33882         * Body text to display (defaults to '').  This can be any valid HTML markup.
33883         */
33884         text : '',
33885        /**
33886         * @cfg {String} cls
33887         * A CSS class to apply to the base quick tip element (defaults to '').
33888         */
33889         cls : '',
33890        /**
33891         * @cfg {Number} width
33892         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33893         * minWidth or maxWidth.
33894         */
33895         width : null,
33896
33897     /**
33898      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33899      * or display QuickTips in a page.
33900      */
33901        init : function(){
33902           tm = Roo.QuickTips;
33903           cfg = tm.tagConfig;
33904           if(!inited){
33905               if(!Roo.isReady){ // allow calling of init() before onReady
33906                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33907                   return;
33908               }
33909               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33910               el.fxDefaults = {stopFx: true};
33911               // maximum custom styling
33912               //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>');
33913               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>');              
33914               tipTitle = el.child('h3');
33915               tipTitle.enableDisplayMode("block");
33916               tipBody = el.child('div.x-tip-bd');
33917               tipBodyText = el.child('div.x-tip-bd-inner');
33918               //bdLeft = el.child('div.x-tip-bd-left');
33919               //bdRight = el.child('div.x-tip-bd-right');
33920               close = el.child('div.x-tip-close');
33921               close.enableDisplayMode("block");
33922               close.on("click", hide);
33923               var d = Roo.get(document);
33924               d.on("mousedown", onDown);
33925               d.on("mouseover", onOver);
33926               d.on("mouseout", onOut);
33927               d.on("mousemove", onMove);
33928               esc = d.addKeyListener(27, hide);
33929               esc.disable();
33930               if(Roo.dd.DD){
33931                   dd = el.initDD("default", null, {
33932                       onDrag : function(){
33933                           el.sync();  
33934                       }
33935                   });
33936                   dd.setHandleElId(tipTitle.id);
33937                   dd.lock();
33938               }
33939               inited = true;
33940           }
33941           this.enable(); 
33942        },
33943
33944     /**
33945      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33946      * are supported:
33947      * <pre>
33948 Property    Type                   Description
33949 ----------  ---------------------  ------------------------------------------------------------------------
33950 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33951      * </ul>
33952      * @param {Object} config The config object
33953      */
33954        register : function(config){
33955            var cs = config instanceof Array ? config : arguments;
33956            for(var i = 0, len = cs.length; i < len; i++) {
33957                var c = cs[i];
33958                var target = c.target;
33959                if(target){
33960                    if(target instanceof Array){
33961                        for(var j = 0, jlen = target.length; j < jlen; j++){
33962                            tagEls[target[j]] = c;
33963                        }
33964                    }else{
33965                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33966                    }
33967                }
33968            }
33969        },
33970
33971     /**
33972      * Removes this quick tip from its element and destroys it.
33973      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33974      */
33975        unregister : function(el){
33976            delete tagEls[Roo.id(el)];
33977        },
33978
33979     /**
33980      * Enable this quick tip.
33981      */
33982        enable : function(){
33983            if(inited && disabled){
33984                locks.pop();
33985                if(locks.length < 1){
33986                    disabled = false;
33987                }
33988            }
33989        },
33990
33991     /**
33992      * Disable this quick tip.
33993      */
33994        disable : function(){
33995           disabled = true;
33996           clearTimeout(showProc);
33997           clearTimeout(hideProc);
33998           clearTimeout(dismissProc);
33999           if(ce){
34000               hide(true);
34001           }
34002           locks.push(1);
34003        },
34004
34005     /**
34006      * Returns true if the quick tip is enabled, else false.
34007      */
34008        isEnabled : function(){
34009             return !disabled;
34010        },
34011
34012         // private
34013        tagConfig : {
34014            namespace : "roo", // was ext?? this may break..
34015            alt_namespace : "ext",
34016            attribute : "qtip",
34017            width : "width",
34018            target : "target",
34019            title : "qtitle",
34020            hide : "hide",
34021            cls : "qclass"
34022        }
34023    };
34024 }();
34025
34026 // backwards compat
34027 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34028  * Based on:
34029  * Ext JS Library 1.1.1
34030  * Copyright(c) 2006-2007, Ext JS, LLC.
34031  *
34032  * Originally Released Under LGPL - original licence link has changed is not relivant.
34033  *
34034  * Fork - LGPL
34035  * <script type="text/javascript">
34036  */
34037  
34038
34039 /**
34040  * @class Roo.tree.TreePanel
34041  * @extends Roo.data.Tree
34042
34043  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34044  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34045  * @cfg {Boolean} enableDD true to enable drag and drop
34046  * @cfg {Boolean} enableDrag true to enable just drag
34047  * @cfg {Boolean} enableDrop true to enable just drop
34048  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34049  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34050  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34051  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34052  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34053  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34054  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34055  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34056  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34057  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34058  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34059  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34060  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34061  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34062  * @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>
34063  * @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>
34064  * 
34065  * @constructor
34066  * @param {String/HTMLElement/Element} el The container element
34067  * @param {Object} config
34068  */
34069 Roo.tree.TreePanel = function(el, config){
34070     var root = false;
34071     var loader = false;
34072     if (config.root) {
34073         root = config.root;
34074         delete config.root;
34075     }
34076     if (config.loader) {
34077         loader = config.loader;
34078         delete config.loader;
34079     }
34080     
34081     Roo.apply(this, config);
34082     Roo.tree.TreePanel.superclass.constructor.call(this);
34083     this.el = Roo.get(el);
34084     this.el.addClass('x-tree');
34085     //console.log(root);
34086     if (root) {
34087         this.setRootNode( Roo.factory(root, Roo.tree));
34088     }
34089     if (loader) {
34090         this.loader = Roo.factory(loader, Roo.tree);
34091     }
34092    /**
34093     * Read-only. The id of the container element becomes this TreePanel's id.
34094     */
34095     this.id = this.el.id;
34096     this.addEvents({
34097         /**
34098         * @event beforeload
34099         * Fires before a node is loaded, return false to cancel
34100         * @param {Node} node The node being loaded
34101         */
34102         "beforeload" : true,
34103         /**
34104         * @event load
34105         * Fires when a node is loaded
34106         * @param {Node} node The node that was loaded
34107         */
34108         "load" : true,
34109         /**
34110         * @event textchange
34111         * Fires when the text for a node is changed
34112         * @param {Node} node The node
34113         * @param {String} text The new text
34114         * @param {String} oldText The old text
34115         */
34116         "textchange" : true,
34117         /**
34118         * @event beforeexpand
34119         * Fires before a node is expanded, return false to cancel.
34120         * @param {Node} node The node
34121         * @param {Boolean} deep
34122         * @param {Boolean} anim
34123         */
34124         "beforeexpand" : true,
34125         /**
34126         * @event beforecollapse
34127         * Fires before a node is collapsed, return false to cancel.
34128         * @param {Node} node The node
34129         * @param {Boolean} deep
34130         * @param {Boolean} anim
34131         */
34132         "beforecollapse" : true,
34133         /**
34134         * @event expand
34135         * Fires when a node is expanded
34136         * @param {Node} node The node
34137         */
34138         "expand" : true,
34139         /**
34140         * @event disabledchange
34141         * Fires when the disabled status of a node changes
34142         * @param {Node} node The node
34143         * @param {Boolean} disabled
34144         */
34145         "disabledchange" : true,
34146         /**
34147         * @event collapse
34148         * Fires when a node is collapsed
34149         * @param {Node} node The node
34150         */
34151         "collapse" : true,
34152         /**
34153         * @event beforeclick
34154         * Fires before click processing on a node. Return false to cancel the default action.
34155         * @param {Node} node The node
34156         * @param {Roo.EventObject} e The event object
34157         */
34158         "beforeclick":true,
34159         /**
34160         * @event checkchange
34161         * Fires when a node with a checkbox's checked property changes
34162         * @param {Node} this This node
34163         * @param {Boolean} checked
34164         */
34165         "checkchange":true,
34166         /**
34167         * @event click
34168         * Fires when a node is clicked
34169         * @param {Node} node The node
34170         * @param {Roo.EventObject} e The event object
34171         */
34172         "click":true,
34173         /**
34174         * @event dblclick
34175         * Fires when a node is double clicked
34176         * @param {Node} node The node
34177         * @param {Roo.EventObject} e The event object
34178         */
34179         "dblclick":true,
34180         /**
34181         * @event contextmenu
34182         * Fires when a node is right clicked
34183         * @param {Node} node The node
34184         * @param {Roo.EventObject} e The event object
34185         */
34186         "contextmenu":true,
34187         /**
34188         * @event beforechildrenrendered
34189         * Fires right before the child nodes for a node are rendered
34190         * @param {Node} node The node
34191         */
34192         "beforechildrenrendered":true,
34193         /**
34194         * @event startdrag
34195         * Fires when a node starts being dragged
34196         * @param {Roo.tree.TreePanel} this
34197         * @param {Roo.tree.TreeNode} node
34198         * @param {event} e The raw browser event
34199         */ 
34200        "startdrag" : true,
34201        /**
34202         * @event enddrag
34203         * Fires when a drag operation is complete
34204         * @param {Roo.tree.TreePanel} this
34205         * @param {Roo.tree.TreeNode} node
34206         * @param {event} e The raw browser event
34207         */
34208        "enddrag" : true,
34209        /**
34210         * @event dragdrop
34211         * Fires when a dragged node is dropped on a valid DD target
34212         * @param {Roo.tree.TreePanel} this
34213         * @param {Roo.tree.TreeNode} node
34214         * @param {DD} dd The dd it was dropped on
34215         * @param {event} e The raw browser event
34216         */
34217        "dragdrop" : true,
34218        /**
34219         * @event beforenodedrop
34220         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34221         * passed to handlers has the following properties:<br />
34222         * <ul style="padding:5px;padding-left:16px;">
34223         * <li>tree - The TreePanel</li>
34224         * <li>target - The node being targeted for the drop</li>
34225         * <li>data - The drag data from the drag source</li>
34226         * <li>point - The point of the drop - append, above or below</li>
34227         * <li>source - The drag source</li>
34228         * <li>rawEvent - Raw mouse event</li>
34229         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34230         * to be inserted by setting them on this object.</li>
34231         * <li>cancel - Set this to true to cancel the drop.</li>
34232         * </ul>
34233         * @param {Object} dropEvent
34234         */
34235        "beforenodedrop" : true,
34236        /**
34237         * @event nodedrop
34238         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34239         * passed to handlers has the following properties:<br />
34240         * <ul style="padding:5px;padding-left:16px;">
34241         * <li>tree - The TreePanel</li>
34242         * <li>target - The node being targeted for the drop</li>
34243         * <li>data - The drag data from the drag source</li>
34244         * <li>point - The point of the drop - append, above or below</li>
34245         * <li>source - The drag source</li>
34246         * <li>rawEvent - Raw mouse event</li>
34247         * <li>dropNode - Dropped node(s).</li>
34248         * </ul>
34249         * @param {Object} dropEvent
34250         */
34251        "nodedrop" : true,
34252         /**
34253         * @event nodedragover
34254         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34255         * passed to handlers has the following properties:<br />
34256         * <ul style="padding:5px;padding-left:16px;">
34257         * <li>tree - The TreePanel</li>
34258         * <li>target - The node being targeted for the drop</li>
34259         * <li>data - The drag data from the drag source</li>
34260         * <li>point - The point of the drop - append, above or below</li>
34261         * <li>source - The drag source</li>
34262         * <li>rawEvent - Raw mouse event</li>
34263         * <li>dropNode - Drop node(s) provided by the source.</li>
34264         * <li>cancel - Set this to true to signal drop not allowed.</li>
34265         * </ul>
34266         * @param {Object} dragOverEvent
34267         */
34268        "nodedragover" : true
34269         
34270     });
34271     if(this.singleExpand){
34272        this.on("beforeexpand", this.restrictExpand, this);
34273     }
34274     if (this.editor) {
34275         this.editor.tree = this;
34276         this.editor = Roo.factory(this.editor, Roo.tree);
34277     }
34278     
34279     if (this.selModel) {
34280         this.selModel = Roo.factory(this.selModel, Roo.tree);
34281     }
34282    
34283 };
34284 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34285     rootVisible : true,
34286     animate: Roo.enableFx,
34287     lines : true,
34288     enableDD : false,
34289     hlDrop : Roo.enableFx,
34290   
34291     renderer: false,
34292     
34293     rendererTip: false,
34294     // private
34295     restrictExpand : function(node){
34296         var p = node.parentNode;
34297         if(p){
34298             if(p.expandedChild && p.expandedChild.parentNode == p){
34299                 p.expandedChild.collapse();
34300             }
34301             p.expandedChild = node;
34302         }
34303     },
34304
34305     // private override
34306     setRootNode : function(node){
34307         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34308         if(!this.rootVisible){
34309             node.ui = new Roo.tree.RootTreeNodeUI(node);
34310         }
34311         return node;
34312     },
34313
34314     /**
34315      * Returns the container element for this TreePanel
34316      */
34317     getEl : function(){
34318         return this.el;
34319     },
34320
34321     /**
34322      * Returns the default TreeLoader for this TreePanel
34323      */
34324     getLoader : function(){
34325         return this.loader;
34326     },
34327
34328     /**
34329      * Expand all nodes
34330      */
34331     expandAll : function(){
34332         this.root.expand(true);
34333     },
34334
34335     /**
34336      * Collapse all nodes
34337      */
34338     collapseAll : function(){
34339         this.root.collapse(true);
34340     },
34341
34342     /**
34343      * Returns the selection model used by this TreePanel
34344      */
34345     getSelectionModel : function(){
34346         if(!this.selModel){
34347             this.selModel = new Roo.tree.DefaultSelectionModel();
34348         }
34349         return this.selModel;
34350     },
34351
34352     /**
34353      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34354      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34355      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34356      * @return {Array}
34357      */
34358     getChecked : function(a, startNode){
34359         startNode = startNode || this.root;
34360         var r = [];
34361         var f = function(){
34362             if(this.attributes.checked){
34363                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34364             }
34365         }
34366         startNode.cascade(f);
34367         return r;
34368     },
34369
34370     /**
34371      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34372      * @param {String} path
34373      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34374      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34375      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34376      */
34377     expandPath : function(path, attr, callback){
34378         attr = attr || "id";
34379         var keys = path.split(this.pathSeparator);
34380         var curNode = this.root;
34381         if(curNode.attributes[attr] != keys[1]){ // invalid root
34382             if(callback){
34383                 callback(false, null);
34384             }
34385             return;
34386         }
34387         var index = 1;
34388         var f = function(){
34389             if(++index == keys.length){
34390                 if(callback){
34391                     callback(true, curNode);
34392                 }
34393                 return;
34394             }
34395             var c = curNode.findChild(attr, keys[index]);
34396             if(!c){
34397                 if(callback){
34398                     callback(false, curNode);
34399                 }
34400                 return;
34401             }
34402             curNode = c;
34403             c.expand(false, false, f);
34404         };
34405         curNode.expand(false, false, f);
34406     },
34407
34408     /**
34409      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34410      * @param {String} path
34411      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34412      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34413      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34414      */
34415     selectPath : function(path, attr, callback){
34416         attr = attr || "id";
34417         var keys = path.split(this.pathSeparator);
34418         var v = keys.pop();
34419         if(keys.length > 0){
34420             var f = function(success, node){
34421                 if(success && node){
34422                     var n = node.findChild(attr, v);
34423                     if(n){
34424                         n.select();
34425                         if(callback){
34426                             callback(true, n);
34427                         }
34428                     }else if(callback){
34429                         callback(false, n);
34430                     }
34431                 }else{
34432                     if(callback){
34433                         callback(false, n);
34434                     }
34435                 }
34436             };
34437             this.expandPath(keys.join(this.pathSeparator), attr, f);
34438         }else{
34439             this.root.select();
34440             if(callback){
34441                 callback(true, this.root);
34442             }
34443         }
34444     },
34445
34446     getTreeEl : function(){
34447         return this.el;
34448     },
34449
34450     /**
34451      * Trigger rendering of this TreePanel
34452      */
34453     render : function(){
34454         if (this.innerCt) {
34455             return this; // stop it rendering more than once!!
34456         }
34457         
34458         this.innerCt = this.el.createChild({tag:"ul",
34459                cls:"x-tree-root-ct " +
34460                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34461
34462         if(this.containerScroll){
34463             Roo.dd.ScrollManager.register(this.el);
34464         }
34465         if((this.enableDD || this.enableDrop) && !this.dropZone){
34466            /**
34467             * The dropZone used by this tree if drop is enabled
34468             * @type Roo.tree.TreeDropZone
34469             */
34470              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34471                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34472            });
34473         }
34474         if((this.enableDD || this.enableDrag) && !this.dragZone){
34475            /**
34476             * The dragZone used by this tree if drag is enabled
34477             * @type Roo.tree.TreeDragZone
34478             */
34479             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34480                ddGroup: this.ddGroup || "TreeDD",
34481                scroll: this.ddScroll
34482            });
34483         }
34484         this.getSelectionModel().init(this);
34485         if (!this.root) {
34486             Roo.log("ROOT not set in tree");
34487             return this;
34488         }
34489         this.root.render();
34490         if(!this.rootVisible){
34491             this.root.renderChildren();
34492         }
34493         return this;
34494     }
34495 });/*
34496  * Based on:
34497  * Ext JS Library 1.1.1
34498  * Copyright(c) 2006-2007, Ext JS, LLC.
34499  *
34500  * Originally Released Under LGPL - original licence link has changed is not relivant.
34501  *
34502  * Fork - LGPL
34503  * <script type="text/javascript">
34504  */
34505  
34506
34507 /**
34508  * @class Roo.tree.DefaultSelectionModel
34509  * @extends Roo.util.Observable
34510  * The default single selection for a TreePanel.
34511  * @param {Object} cfg Configuration
34512  */
34513 Roo.tree.DefaultSelectionModel = function(cfg){
34514    this.selNode = null;
34515    
34516    
34517    
34518    this.addEvents({
34519        /**
34520         * @event selectionchange
34521         * Fires when the selected node changes
34522         * @param {DefaultSelectionModel} this
34523         * @param {TreeNode} node the new selection
34524         */
34525        "selectionchange" : true,
34526
34527        /**
34528         * @event beforeselect
34529         * Fires before the selected node changes, return false to cancel the change
34530         * @param {DefaultSelectionModel} this
34531         * @param {TreeNode} node the new selection
34532         * @param {TreeNode} node the old selection
34533         */
34534        "beforeselect" : true
34535    });
34536    
34537     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34538 };
34539
34540 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34541     init : function(tree){
34542         this.tree = tree;
34543         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34544         tree.on("click", this.onNodeClick, this);
34545     },
34546     
34547     onNodeClick : function(node, e){
34548         if (e.ctrlKey && this.selNode == node)  {
34549             this.unselect(node);
34550             return;
34551         }
34552         this.select(node);
34553     },
34554     
34555     /**
34556      * Select a node.
34557      * @param {TreeNode} node The node to select
34558      * @return {TreeNode} The selected node
34559      */
34560     select : function(node){
34561         var last = this.selNode;
34562         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34563             if(last){
34564                 last.ui.onSelectedChange(false);
34565             }
34566             this.selNode = node;
34567             node.ui.onSelectedChange(true);
34568             this.fireEvent("selectionchange", this, node, last);
34569         }
34570         return node;
34571     },
34572     
34573     /**
34574      * Deselect a node.
34575      * @param {TreeNode} node The node to unselect
34576      */
34577     unselect : function(node){
34578         if(this.selNode == node){
34579             this.clearSelections();
34580         }    
34581     },
34582     
34583     /**
34584      * Clear all selections
34585      */
34586     clearSelections : function(){
34587         var n = this.selNode;
34588         if(n){
34589             n.ui.onSelectedChange(false);
34590             this.selNode = null;
34591             this.fireEvent("selectionchange", this, null);
34592         }
34593         return n;
34594     },
34595     
34596     /**
34597      * Get the selected node
34598      * @return {TreeNode} The selected node
34599      */
34600     getSelectedNode : function(){
34601         return this.selNode;    
34602     },
34603     
34604     /**
34605      * Returns true if the node is selected
34606      * @param {TreeNode} node The node to check
34607      * @return {Boolean}
34608      */
34609     isSelected : function(node){
34610         return this.selNode == node;  
34611     },
34612
34613     /**
34614      * Selects the node above the selected node in the tree, intelligently walking the nodes
34615      * @return TreeNode The new selection
34616      */
34617     selectPrevious : function(){
34618         var s = this.selNode || this.lastSelNode;
34619         if(!s){
34620             return null;
34621         }
34622         var ps = s.previousSibling;
34623         if(ps){
34624             if(!ps.isExpanded() || ps.childNodes.length < 1){
34625                 return this.select(ps);
34626             } else{
34627                 var lc = ps.lastChild;
34628                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34629                     lc = lc.lastChild;
34630                 }
34631                 return this.select(lc);
34632             }
34633         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34634             return this.select(s.parentNode);
34635         }
34636         return null;
34637     },
34638
34639     /**
34640      * Selects the node above the selected node in the tree, intelligently walking the nodes
34641      * @return TreeNode The new selection
34642      */
34643     selectNext : function(){
34644         var s = this.selNode || this.lastSelNode;
34645         if(!s){
34646             return null;
34647         }
34648         if(s.firstChild && s.isExpanded()){
34649              return this.select(s.firstChild);
34650          }else if(s.nextSibling){
34651              return this.select(s.nextSibling);
34652          }else if(s.parentNode){
34653             var newS = null;
34654             s.parentNode.bubble(function(){
34655                 if(this.nextSibling){
34656                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34657                     return false;
34658                 }
34659             });
34660             return newS;
34661          }
34662         return null;
34663     },
34664
34665     onKeyDown : function(e){
34666         var s = this.selNode || this.lastSelNode;
34667         // undesirable, but required
34668         var sm = this;
34669         if(!s){
34670             return;
34671         }
34672         var k = e.getKey();
34673         switch(k){
34674              case e.DOWN:
34675                  e.stopEvent();
34676                  this.selectNext();
34677              break;
34678              case e.UP:
34679                  e.stopEvent();
34680                  this.selectPrevious();
34681              break;
34682              case e.RIGHT:
34683                  e.preventDefault();
34684                  if(s.hasChildNodes()){
34685                      if(!s.isExpanded()){
34686                          s.expand();
34687                      }else if(s.firstChild){
34688                          this.select(s.firstChild, e);
34689                      }
34690                  }
34691              break;
34692              case e.LEFT:
34693                  e.preventDefault();
34694                  if(s.hasChildNodes() && s.isExpanded()){
34695                      s.collapse();
34696                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34697                      this.select(s.parentNode, e);
34698                  }
34699              break;
34700         };
34701     }
34702 });
34703
34704 /**
34705  * @class Roo.tree.MultiSelectionModel
34706  * @extends Roo.util.Observable
34707  * Multi selection for a TreePanel.
34708  * @param {Object} cfg Configuration
34709  */
34710 Roo.tree.MultiSelectionModel = function(){
34711    this.selNodes = [];
34712    this.selMap = {};
34713    this.addEvents({
34714        /**
34715         * @event selectionchange
34716         * Fires when the selected nodes change
34717         * @param {MultiSelectionModel} this
34718         * @param {Array} nodes Array of the selected nodes
34719         */
34720        "selectionchange" : true
34721    });
34722    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34723    
34724 };
34725
34726 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34727     init : function(tree){
34728         this.tree = tree;
34729         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34730         tree.on("click", this.onNodeClick, this);
34731     },
34732     
34733     onNodeClick : function(node, e){
34734         this.select(node, e, e.ctrlKey);
34735     },
34736     
34737     /**
34738      * Select a node.
34739      * @param {TreeNode} node The node to select
34740      * @param {EventObject} e (optional) An event associated with the selection
34741      * @param {Boolean} keepExisting True to retain existing selections
34742      * @return {TreeNode} The selected node
34743      */
34744     select : function(node, e, keepExisting){
34745         if(keepExisting !== true){
34746             this.clearSelections(true);
34747         }
34748         if(this.isSelected(node)){
34749             this.lastSelNode = node;
34750             return node;
34751         }
34752         this.selNodes.push(node);
34753         this.selMap[node.id] = node;
34754         this.lastSelNode = node;
34755         node.ui.onSelectedChange(true);
34756         this.fireEvent("selectionchange", this, this.selNodes);
34757         return node;
34758     },
34759     
34760     /**
34761      * Deselect a node.
34762      * @param {TreeNode} node The node to unselect
34763      */
34764     unselect : function(node){
34765         if(this.selMap[node.id]){
34766             node.ui.onSelectedChange(false);
34767             var sn = this.selNodes;
34768             var index = -1;
34769             if(sn.indexOf){
34770                 index = sn.indexOf(node);
34771             }else{
34772                 for(var i = 0, len = sn.length; i < len; i++){
34773                     if(sn[i] == node){
34774                         index = i;
34775                         break;
34776                     }
34777                 }
34778             }
34779             if(index != -1){
34780                 this.selNodes.splice(index, 1);
34781             }
34782             delete this.selMap[node.id];
34783             this.fireEvent("selectionchange", this, this.selNodes);
34784         }
34785     },
34786     
34787     /**
34788      * Clear all selections
34789      */
34790     clearSelections : function(suppressEvent){
34791         var sn = this.selNodes;
34792         if(sn.length > 0){
34793             for(var i = 0, len = sn.length; i < len; i++){
34794                 sn[i].ui.onSelectedChange(false);
34795             }
34796             this.selNodes = [];
34797             this.selMap = {};
34798             if(suppressEvent !== true){
34799                 this.fireEvent("selectionchange", this, this.selNodes);
34800             }
34801         }
34802     },
34803     
34804     /**
34805      * Returns true if the node is selected
34806      * @param {TreeNode} node The node to check
34807      * @return {Boolean}
34808      */
34809     isSelected : function(node){
34810         return this.selMap[node.id] ? true : false;  
34811     },
34812     
34813     /**
34814      * Returns an array of the selected nodes
34815      * @return {Array}
34816      */
34817     getSelectedNodes : function(){
34818         return this.selNodes;    
34819     },
34820
34821     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34822
34823     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34824
34825     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34826 });/*
34827  * Based on:
34828  * Ext JS Library 1.1.1
34829  * Copyright(c) 2006-2007, Ext JS, LLC.
34830  *
34831  * Originally Released Under LGPL - original licence link has changed is not relivant.
34832  *
34833  * Fork - LGPL
34834  * <script type="text/javascript">
34835  */
34836  
34837 /**
34838  * @class Roo.tree.TreeNode
34839  * @extends Roo.data.Node
34840  * @cfg {String} text The text for this node
34841  * @cfg {Boolean} expanded true to start the node expanded
34842  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34843  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34844  * @cfg {Boolean} disabled true to start the node disabled
34845  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34846  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
34847  * @cfg {String} cls A css class to be added to the node
34848  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34849  * @cfg {String} href URL of the link used for the node (defaults to #)
34850  * @cfg {String} hrefTarget target frame for the link
34851  * @cfg {String} qtip An Ext QuickTip for the node
34852  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34853  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34854  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34855  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34856  * (defaults to undefined with no checkbox rendered)
34857  * @constructor
34858  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34859  */
34860 Roo.tree.TreeNode = function(attributes){
34861     attributes = attributes || {};
34862     if(typeof attributes == "string"){
34863         attributes = {text: attributes};
34864     }
34865     this.childrenRendered = false;
34866     this.rendered = false;
34867     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34868     this.expanded = attributes.expanded === true;
34869     this.isTarget = attributes.isTarget !== false;
34870     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34871     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34872
34873     /**
34874      * Read-only. The text for this node. To change it use setText().
34875      * @type String
34876      */
34877     this.text = attributes.text;
34878     /**
34879      * True if this node is disabled.
34880      * @type Boolean
34881      */
34882     this.disabled = attributes.disabled === true;
34883
34884     this.addEvents({
34885         /**
34886         * @event textchange
34887         * Fires when the text for this node is changed
34888         * @param {Node} this This node
34889         * @param {String} text The new text
34890         * @param {String} oldText The old text
34891         */
34892         "textchange" : true,
34893         /**
34894         * @event beforeexpand
34895         * Fires before this node is expanded, return false to cancel.
34896         * @param {Node} this This node
34897         * @param {Boolean} deep
34898         * @param {Boolean} anim
34899         */
34900         "beforeexpand" : true,
34901         /**
34902         * @event beforecollapse
34903         * Fires before this node is collapsed, return false to cancel.
34904         * @param {Node} this This node
34905         * @param {Boolean} deep
34906         * @param {Boolean} anim
34907         */
34908         "beforecollapse" : true,
34909         /**
34910         * @event expand
34911         * Fires when this node is expanded
34912         * @param {Node} this This node
34913         */
34914         "expand" : true,
34915         /**
34916         * @event disabledchange
34917         * Fires when the disabled status of this node changes
34918         * @param {Node} this This node
34919         * @param {Boolean} disabled
34920         */
34921         "disabledchange" : true,
34922         /**
34923         * @event collapse
34924         * Fires when this node is collapsed
34925         * @param {Node} this This node
34926         */
34927         "collapse" : true,
34928         /**
34929         * @event beforeclick
34930         * Fires before click processing. Return false to cancel the default action.
34931         * @param {Node} this This node
34932         * @param {Roo.EventObject} e The event object
34933         */
34934         "beforeclick":true,
34935         /**
34936         * @event checkchange
34937         * Fires when a node with a checkbox's checked property changes
34938         * @param {Node} this This node
34939         * @param {Boolean} checked
34940         */
34941         "checkchange":true,
34942         /**
34943         * @event click
34944         * Fires when this node is clicked
34945         * @param {Node} this This node
34946         * @param {Roo.EventObject} e The event object
34947         */
34948         "click":true,
34949         /**
34950         * @event dblclick
34951         * Fires when this node is double clicked
34952         * @param {Node} this This node
34953         * @param {Roo.EventObject} e The event object
34954         */
34955         "dblclick":true,
34956         /**
34957         * @event contextmenu
34958         * Fires when this node is right clicked
34959         * @param {Node} this This node
34960         * @param {Roo.EventObject} e The event object
34961         */
34962         "contextmenu":true,
34963         /**
34964         * @event beforechildrenrendered
34965         * Fires right before the child nodes for this node are rendered
34966         * @param {Node} this This node
34967         */
34968         "beforechildrenrendered":true
34969     });
34970
34971     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34972
34973     /**
34974      * Read-only. The UI for this node
34975      * @type TreeNodeUI
34976      */
34977     this.ui = new uiClass(this);
34978     
34979     // finally support items[]
34980     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
34981         return;
34982     }
34983     
34984     
34985     Roo.each(this.attributes.items, function(c) {
34986         this.appendChild(Roo.factory(c,Roo.Tree));
34987     }, this);
34988     delete this.attributes.items;
34989     
34990     
34991     
34992 };
34993 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
34994     preventHScroll: true,
34995     /**
34996      * Returns true if this node is expanded
34997      * @return {Boolean}
34998      */
34999     isExpanded : function(){
35000         return this.expanded;
35001     },
35002
35003     /**
35004      * Returns the UI object for this node
35005      * @return {TreeNodeUI}
35006      */
35007     getUI : function(){
35008         return this.ui;
35009     },
35010
35011     // private override
35012     setFirstChild : function(node){
35013         var of = this.firstChild;
35014         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35015         if(this.childrenRendered && of && node != of){
35016             of.renderIndent(true, true);
35017         }
35018         if(this.rendered){
35019             this.renderIndent(true, true);
35020         }
35021     },
35022
35023     // private override
35024     setLastChild : function(node){
35025         var ol = this.lastChild;
35026         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35027         if(this.childrenRendered && ol && node != ol){
35028             ol.renderIndent(true, true);
35029         }
35030         if(this.rendered){
35031             this.renderIndent(true, true);
35032         }
35033     },
35034
35035     // these methods are overridden to provide lazy rendering support
35036     // private override
35037     appendChild : function()
35038     {
35039         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35040         if(node && this.childrenRendered){
35041             node.render();
35042         }
35043         this.ui.updateExpandIcon();
35044         return node;
35045     },
35046
35047     // private override
35048     removeChild : function(node){
35049         this.ownerTree.getSelectionModel().unselect(node);
35050         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35051         // if it's been rendered remove dom node
35052         if(this.childrenRendered){
35053             node.ui.remove();
35054         }
35055         if(this.childNodes.length < 1){
35056             this.collapse(false, false);
35057         }else{
35058             this.ui.updateExpandIcon();
35059         }
35060         if(!this.firstChild) {
35061             this.childrenRendered = false;
35062         }
35063         return node;
35064     },
35065
35066     // private override
35067     insertBefore : function(node, refNode){
35068         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35069         if(newNode && refNode && this.childrenRendered){
35070             node.render();
35071         }
35072         this.ui.updateExpandIcon();
35073         return newNode;
35074     },
35075
35076     /**
35077      * Sets the text for this node
35078      * @param {String} text
35079      */
35080     setText : function(text){
35081         var oldText = this.text;
35082         this.text = text;
35083         this.attributes.text = text;
35084         if(this.rendered){ // event without subscribing
35085             this.ui.onTextChange(this, text, oldText);
35086         }
35087         this.fireEvent("textchange", this, text, oldText);
35088     },
35089
35090     /**
35091      * Triggers selection of this node
35092      */
35093     select : function(){
35094         this.getOwnerTree().getSelectionModel().select(this);
35095     },
35096
35097     /**
35098      * Triggers deselection of this node
35099      */
35100     unselect : function(){
35101         this.getOwnerTree().getSelectionModel().unselect(this);
35102     },
35103
35104     /**
35105      * Returns true if this node is selected
35106      * @return {Boolean}
35107      */
35108     isSelected : function(){
35109         return this.getOwnerTree().getSelectionModel().isSelected(this);
35110     },
35111
35112     /**
35113      * Expand this node.
35114      * @param {Boolean} deep (optional) True to expand all children as well
35115      * @param {Boolean} anim (optional) false to cancel the default animation
35116      * @param {Function} callback (optional) A callback to be called when
35117      * expanding this node completes (does not wait for deep expand to complete).
35118      * Called with 1 parameter, this node.
35119      */
35120     expand : function(deep, anim, callback){
35121         if(!this.expanded){
35122             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35123                 return;
35124             }
35125             if(!this.childrenRendered){
35126                 this.renderChildren();
35127             }
35128             this.expanded = true;
35129             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
35130                 this.ui.animExpand(function(){
35131                     this.fireEvent("expand", this);
35132                     if(typeof callback == "function"){
35133                         callback(this);
35134                     }
35135                     if(deep === true){
35136                         this.expandChildNodes(true);
35137                     }
35138                 }.createDelegate(this));
35139                 return;
35140             }else{
35141                 this.ui.expand();
35142                 this.fireEvent("expand", this);
35143                 if(typeof callback == "function"){
35144                     callback(this);
35145                 }
35146             }
35147         }else{
35148            if(typeof callback == "function"){
35149                callback(this);
35150            }
35151         }
35152         if(deep === true){
35153             this.expandChildNodes(true);
35154         }
35155     },
35156
35157     isHiddenRoot : function(){
35158         return this.isRoot && !this.getOwnerTree().rootVisible;
35159     },
35160
35161     /**
35162      * Collapse this node.
35163      * @param {Boolean} deep (optional) True to collapse all children as well
35164      * @param {Boolean} anim (optional) false to cancel the default animation
35165      */
35166     collapse : function(deep, anim){
35167         if(this.expanded && !this.isHiddenRoot()){
35168             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35169                 return;
35170             }
35171             this.expanded = false;
35172             if((this.getOwnerTree().animate && anim !== false) || anim){
35173                 this.ui.animCollapse(function(){
35174                     this.fireEvent("collapse", this);
35175                     if(deep === true){
35176                         this.collapseChildNodes(true);
35177                     }
35178                 }.createDelegate(this));
35179                 return;
35180             }else{
35181                 this.ui.collapse();
35182                 this.fireEvent("collapse", this);
35183             }
35184         }
35185         if(deep === true){
35186             var cs = this.childNodes;
35187             for(var i = 0, len = cs.length; i < len; i++) {
35188                 cs[i].collapse(true, false);
35189             }
35190         }
35191     },
35192
35193     // private
35194     delayedExpand : function(delay){
35195         if(!this.expandProcId){
35196             this.expandProcId = this.expand.defer(delay, this);
35197         }
35198     },
35199
35200     // private
35201     cancelExpand : function(){
35202         if(this.expandProcId){
35203             clearTimeout(this.expandProcId);
35204         }
35205         this.expandProcId = false;
35206     },
35207
35208     /**
35209      * Toggles expanded/collapsed state of the node
35210      */
35211     toggle : function(){
35212         if(this.expanded){
35213             this.collapse();
35214         }else{
35215             this.expand();
35216         }
35217     },
35218
35219     /**
35220      * Ensures all parent nodes are expanded
35221      */
35222     ensureVisible : function(callback){
35223         var tree = this.getOwnerTree();
35224         tree.expandPath(this.parentNode.getPath(), false, function(){
35225             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35226             Roo.callback(callback);
35227         }.createDelegate(this));
35228     },
35229
35230     /**
35231      * Expand all child nodes
35232      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35233      */
35234     expandChildNodes : function(deep){
35235         var cs = this.childNodes;
35236         for(var i = 0, len = cs.length; i < len; i++) {
35237                 cs[i].expand(deep);
35238         }
35239     },
35240
35241     /**
35242      * Collapse all child nodes
35243      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35244      */
35245     collapseChildNodes : function(deep){
35246         var cs = this.childNodes;
35247         for(var i = 0, len = cs.length; i < len; i++) {
35248                 cs[i].collapse(deep);
35249         }
35250     },
35251
35252     /**
35253      * Disables this node
35254      */
35255     disable : function(){
35256         this.disabled = true;
35257         this.unselect();
35258         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35259             this.ui.onDisableChange(this, true);
35260         }
35261         this.fireEvent("disabledchange", this, true);
35262     },
35263
35264     /**
35265      * Enables this node
35266      */
35267     enable : function(){
35268         this.disabled = false;
35269         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35270             this.ui.onDisableChange(this, false);
35271         }
35272         this.fireEvent("disabledchange", this, false);
35273     },
35274
35275     // private
35276     renderChildren : function(suppressEvent){
35277         if(suppressEvent !== false){
35278             this.fireEvent("beforechildrenrendered", this);
35279         }
35280         var cs = this.childNodes;
35281         for(var i = 0, len = cs.length; i < len; i++){
35282             cs[i].render(true);
35283         }
35284         this.childrenRendered = true;
35285     },
35286
35287     // private
35288     sort : function(fn, scope){
35289         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35290         if(this.childrenRendered){
35291             var cs = this.childNodes;
35292             for(var i = 0, len = cs.length; i < len; i++){
35293                 cs[i].render(true);
35294             }
35295         }
35296     },
35297
35298     // private
35299     render : function(bulkRender){
35300         this.ui.render(bulkRender);
35301         if(!this.rendered){
35302             this.rendered = true;
35303             if(this.expanded){
35304                 this.expanded = false;
35305                 this.expand(false, false);
35306             }
35307         }
35308     },
35309
35310     // private
35311     renderIndent : function(deep, refresh){
35312         if(refresh){
35313             this.ui.childIndent = null;
35314         }
35315         this.ui.renderIndent();
35316         if(deep === true && this.childrenRendered){
35317             var cs = this.childNodes;
35318             for(var i = 0, len = cs.length; i < len; i++){
35319                 cs[i].renderIndent(true, refresh);
35320             }
35321         }
35322     }
35323 });/*
35324  * Based on:
35325  * Ext JS Library 1.1.1
35326  * Copyright(c) 2006-2007, Ext JS, LLC.
35327  *
35328  * Originally Released Under LGPL - original licence link has changed is not relivant.
35329  *
35330  * Fork - LGPL
35331  * <script type="text/javascript">
35332  */
35333  
35334 /**
35335  * @class Roo.tree.AsyncTreeNode
35336  * @extends Roo.tree.TreeNode
35337  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35338  * @constructor
35339  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35340  */
35341  Roo.tree.AsyncTreeNode = function(config){
35342     this.loaded = false;
35343     this.loading = false;
35344     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35345     /**
35346     * @event beforeload
35347     * Fires before this node is loaded, return false to cancel
35348     * @param {Node} this This node
35349     */
35350     this.addEvents({'beforeload':true, 'load': true});
35351     /**
35352     * @event load
35353     * Fires when this node is loaded
35354     * @param {Node} this This node
35355     */
35356     /**
35357      * The loader used by this node (defaults to using the tree's defined loader)
35358      * @type TreeLoader
35359      * @property loader
35360      */
35361 };
35362 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35363     expand : function(deep, anim, callback){
35364         if(this.loading){ // if an async load is already running, waiting til it's done
35365             var timer;
35366             var f = function(){
35367                 if(!this.loading){ // done loading
35368                     clearInterval(timer);
35369                     this.expand(deep, anim, callback);
35370                 }
35371             }.createDelegate(this);
35372             timer = setInterval(f, 200);
35373             return;
35374         }
35375         if(!this.loaded){
35376             if(this.fireEvent("beforeload", this) === false){
35377                 return;
35378             }
35379             this.loading = true;
35380             this.ui.beforeLoad(this);
35381             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35382             if(loader){
35383                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35384                 return;
35385             }
35386         }
35387         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35388     },
35389     
35390     /**
35391      * Returns true if this node is currently loading
35392      * @return {Boolean}
35393      */
35394     isLoading : function(){
35395         return this.loading;  
35396     },
35397     
35398     loadComplete : function(deep, anim, callback){
35399         this.loading = false;
35400         this.loaded = true;
35401         this.ui.afterLoad(this);
35402         this.fireEvent("load", this);
35403         this.expand(deep, anim, callback);
35404     },
35405     
35406     /**
35407      * Returns true if this node has been loaded
35408      * @return {Boolean}
35409      */
35410     isLoaded : function(){
35411         return this.loaded;
35412     },
35413     
35414     hasChildNodes : function(){
35415         if(!this.isLeaf() && !this.loaded){
35416             return true;
35417         }else{
35418             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35419         }
35420     },
35421
35422     /**
35423      * Trigger a reload for this node
35424      * @param {Function} callback
35425      */
35426     reload : function(callback){
35427         this.collapse(false, false);
35428         while(this.firstChild){
35429             this.removeChild(this.firstChild);
35430         }
35431         this.childrenRendered = false;
35432         this.loaded = false;
35433         if(this.isHiddenRoot()){
35434             this.expanded = false;
35435         }
35436         this.expand(false, false, callback);
35437     }
35438 });/*
35439  * Based on:
35440  * Ext JS Library 1.1.1
35441  * Copyright(c) 2006-2007, Ext JS, LLC.
35442  *
35443  * Originally Released Under LGPL - original licence link has changed is not relivant.
35444  *
35445  * Fork - LGPL
35446  * <script type="text/javascript">
35447  */
35448  
35449 /**
35450  * @class Roo.tree.TreeNodeUI
35451  * @constructor
35452  * @param {Object} node The node to render
35453  * The TreeNode UI implementation is separate from the
35454  * tree implementation. Unless you are customizing the tree UI,
35455  * you should never have to use this directly.
35456  */
35457 Roo.tree.TreeNodeUI = function(node){
35458     this.node = node;
35459     this.rendered = false;
35460     this.animating = false;
35461     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35462 };
35463
35464 Roo.tree.TreeNodeUI.prototype = {
35465     removeChild : function(node){
35466         if(this.rendered){
35467             this.ctNode.removeChild(node.ui.getEl());
35468         }
35469     },
35470
35471     beforeLoad : function(){
35472          this.addClass("x-tree-node-loading");
35473     },
35474
35475     afterLoad : function(){
35476          this.removeClass("x-tree-node-loading");
35477     },
35478
35479     onTextChange : function(node, text, oldText){
35480         if(this.rendered){
35481             this.textNode.innerHTML = text;
35482         }
35483     },
35484
35485     onDisableChange : function(node, state){
35486         this.disabled = state;
35487         if(state){
35488             this.addClass("x-tree-node-disabled");
35489         }else{
35490             this.removeClass("x-tree-node-disabled");
35491         }
35492     },
35493
35494     onSelectedChange : function(state){
35495         if(state){
35496             this.focus();
35497             this.addClass("x-tree-selected");
35498         }else{
35499             //this.blur();
35500             this.removeClass("x-tree-selected");
35501         }
35502     },
35503
35504     onMove : function(tree, node, oldParent, newParent, index, refNode){
35505         this.childIndent = null;
35506         if(this.rendered){
35507             var targetNode = newParent.ui.getContainer();
35508             if(!targetNode){//target not rendered
35509                 this.holder = document.createElement("div");
35510                 this.holder.appendChild(this.wrap);
35511                 return;
35512             }
35513             var insertBefore = refNode ? refNode.ui.getEl() : null;
35514             if(insertBefore){
35515                 targetNode.insertBefore(this.wrap, insertBefore);
35516             }else{
35517                 targetNode.appendChild(this.wrap);
35518             }
35519             this.node.renderIndent(true);
35520         }
35521     },
35522
35523     addClass : function(cls){
35524         if(this.elNode){
35525             Roo.fly(this.elNode).addClass(cls);
35526         }
35527     },
35528
35529     removeClass : function(cls){
35530         if(this.elNode){
35531             Roo.fly(this.elNode).removeClass(cls);
35532         }
35533     },
35534
35535     remove : function(){
35536         if(this.rendered){
35537             this.holder = document.createElement("div");
35538             this.holder.appendChild(this.wrap);
35539         }
35540     },
35541
35542     fireEvent : function(){
35543         return this.node.fireEvent.apply(this.node, arguments);
35544     },
35545
35546     initEvents : function(){
35547         this.node.on("move", this.onMove, this);
35548         var E = Roo.EventManager;
35549         var a = this.anchor;
35550
35551         var el = Roo.fly(a, '_treeui');
35552
35553         if(Roo.isOpera){ // opera render bug ignores the CSS
35554             el.setStyle("text-decoration", "none");
35555         }
35556
35557         el.on("click", this.onClick, this);
35558         el.on("dblclick", this.onDblClick, this);
35559
35560         if(this.checkbox){
35561             Roo.EventManager.on(this.checkbox,
35562                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35563         }
35564
35565         el.on("contextmenu", this.onContextMenu, this);
35566
35567         var icon = Roo.fly(this.iconNode);
35568         icon.on("click", this.onClick, this);
35569         icon.on("dblclick", this.onDblClick, this);
35570         icon.on("contextmenu", this.onContextMenu, this);
35571         E.on(this.ecNode, "click", this.ecClick, this, true);
35572
35573         if(this.node.disabled){
35574             this.addClass("x-tree-node-disabled");
35575         }
35576         if(this.node.hidden){
35577             this.addClass("x-tree-node-disabled");
35578         }
35579         var ot = this.node.getOwnerTree();
35580         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
35581         if(dd && (!this.node.isRoot || ot.rootVisible)){
35582             Roo.dd.Registry.register(this.elNode, {
35583                 node: this.node,
35584                 handles: this.getDDHandles(),
35585                 isHandle: false
35586             });
35587         }
35588     },
35589
35590     getDDHandles : function(){
35591         return [this.iconNode, this.textNode];
35592     },
35593
35594     hide : function(){
35595         if(this.rendered){
35596             this.wrap.style.display = "none";
35597         }
35598     },
35599
35600     show : function(){
35601         if(this.rendered){
35602             this.wrap.style.display = "";
35603         }
35604     },
35605
35606     onContextMenu : function(e){
35607         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35608             e.preventDefault();
35609             this.focus();
35610             this.fireEvent("contextmenu", this.node, e);
35611         }
35612     },
35613
35614     onClick : function(e){
35615         if(this.dropping){
35616             e.stopEvent();
35617             return;
35618         }
35619         if(this.fireEvent("beforeclick", this.node, e) !== false){
35620             if(!this.disabled && this.node.attributes.href){
35621                 this.fireEvent("click", this.node, e);
35622                 return;
35623             }
35624             e.preventDefault();
35625             if(this.disabled){
35626                 return;
35627             }
35628
35629             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35630                 this.node.toggle();
35631             }
35632
35633             this.fireEvent("click", this.node, e);
35634         }else{
35635             e.stopEvent();
35636         }
35637     },
35638
35639     onDblClick : function(e){
35640         e.preventDefault();
35641         if(this.disabled){
35642             return;
35643         }
35644         if(this.checkbox){
35645             this.toggleCheck();
35646         }
35647         if(!this.animating && this.node.hasChildNodes()){
35648             this.node.toggle();
35649         }
35650         this.fireEvent("dblclick", this.node, e);
35651     },
35652
35653     onCheckChange : function(){
35654         var checked = this.checkbox.checked;
35655         this.node.attributes.checked = checked;
35656         this.fireEvent('checkchange', this.node, checked);
35657     },
35658
35659     ecClick : function(e){
35660         if(!this.animating && this.node.hasChildNodes()){
35661             this.node.toggle();
35662         }
35663     },
35664
35665     startDrop : function(){
35666         this.dropping = true;
35667     },
35668
35669     // delayed drop so the click event doesn't get fired on a drop
35670     endDrop : function(){
35671        setTimeout(function(){
35672            this.dropping = false;
35673        }.createDelegate(this), 50);
35674     },
35675
35676     expand : function(){
35677         this.updateExpandIcon();
35678         this.ctNode.style.display = "";
35679     },
35680
35681     focus : function(){
35682         if(!this.node.preventHScroll){
35683             try{this.anchor.focus();
35684             }catch(e){}
35685         }else if(!Roo.isIE){
35686             try{
35687                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35688                 var l = noscroll.scrollLeft;
35689                 this.anchor.focus();
35690                 noscroll.scrollLeft = l;
35691             }catch(e){}
35692         }
35693     },
35694
35695     toggleCheck : function(value){
35696         var cb = this.checkbox;
35697         if(cb){
35698             cb.checked = (value === undefined ? !cb.checked : value);
35699         }
35700     },
35701
35702     blur : function(){
35703         try{
35704             this.anchor.blur();
35705         }catch(e){}
35706     },
35707
35708     animExpand : function(callback){
35709         var ct = Roo.get(this.ctNode);
35710         ct.stopFx();
35711         if(!this.node.hasChildNodes()){
35712             this.updateExpandIcon();
35713             this.ctNode.style.display = "";
35714             Roo.callback(callback);
35715             return;
35716         }
35717         this.animating = true;
35718         this.updateExpandIcon();
35719
35720         ct.slideIn('t', {
35721            callback : function(){
35722                this.animating = false;
35723                Roo.callback(callback);
35724             },
35725             scope: this,
35726             duration: this.node.ownerTree.duration || .25
35727         });
35728     },
35729
35730     highlight : function(){
35731         var tree = this.node.getOwnerTree();
35732         Roo.fly(this.wrap).highlight(
35733             tree.hlColor || "C3DAF9",
35734             {endColor: tree.hlBaseColor}
35735         );
35736     },
35737
35738     collapse : function(){
35739         this.updateExpandIcon();
35740         this.ctNode.style.display = "none";
35741     },
35742
35743     animCollapse : function(callback){
35744         var ct = Roo.get(this.ctNode);
35745         ct.enableDisplayMode('block');
35746         ct.stopFx();
35747
35748         this.animating = true;
35749         this.updateExpandIcon();
35750
35751         ct.slideOut('t', {
35752             callback : function(){
35753                this.animating = false;
35754                Roo.callback(callback);
35755             },
35756             scope: this,
35757             duration: this.node.ownerTree.duration || .25
35758         });
35759     },
35760
35761     getContainer : function(){
35762         return this.ctNode;
35763     },
35764
35765     getEl : function(){
35766         return this.wrap;
35767     },
35768
35769     appendDDGhost : function(ghostNode){
35770         ghostNode.appendChild(this.elNode.cloneNode(true));
35771     },
35772
35773     getDDRepairXY : function(){
35774         return Roo.lib.Dom.getXY(this.iconNode);
35775     },
35776
35777     onRender : function(){
35778         this.render();
35779     },
35780
35781     render : function(bulkRender){
35782         var n = this.node, a = n.attributes;
35783         var targetNode = n.parentNode ?
35784               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35785
35786         if(!this.rendered){
35787             this.rendered = true;
35788
35789             this.renderElements(n, a, targetNode, bulkRender);
35790
35791             if(a.qtip){
35792                if(this.textNode.setAttributeNS){
35793                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35794                    if(a.qtipTitle){
35795                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35796                    }
35797                }else{
35798                    this.textNode.setAttribute("ext:qtip", a.qtip);
35799                    if(a.qtipTitle){
35800                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35801                    }
35802                }
35803             }else if(a.qtipCfg){
35804                 a.qtipCfg.target = Roo.id(this.textNode);
35805                 Roo.QuickTips.register(a.qtipCfg);
35806             }
35807             this.initEvents();
35808             if(!this.node.expanded){
35809                 this.updateExpandIcon();
35810             }
35811         }else{
35812             if(bulkRender === true) {
35813                 targetNode.appendChild(this.wrap);
35814             }
35815         }
35816     },
35817
35818     renderElements : function(n, a, targetNode, bulkRender)
35819     {
35820         // add some indent caching, this helps performance when rendering a large tree
35821         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35822         var t = n.getOwnerTree();
35823         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35824         if (typeof(n.attributes.html) != 'undefined') {
35825             txt = n.attributes.html;
35826         }
35827         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
35828         var cb = typeof a.checked == 'boolean';
35829         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35830         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35831             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35832             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35833             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35834             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35835             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35836              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35837                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35838             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35839             "</li>"];
35840
35841         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35842             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35843                                 n.nextSibling.ui.getEl(), buf.join(""));
35844         }else{
35845             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35846         }
35847
35848         this.elNode = this.wrap.childNodes[0];
35849         this.ctNode = this.wrap.childNodes[1];
35850         var cs = this.elNode.childNodes;
35851         this.indentNode = cs[0];
35852         this.ecNode = cs[1];
35853         this.iconNode = cs[2];
35854         var index = 3;
35855         if(cb){
35856             this.checkbox = cs[3];
35857             index++;
35858         }
35859         this.anchor = cs[index];
35860         this.textNode = cs[index].firstChild;
35861     },
35862
35863     getAnchor : function(){
35864         return this.anchor;
35865     },
35866
35867     getTextEl : function(){
35868         return this.textNode;
35869     },
35870
35871     getIconEl : function(){
35872         return this.iconNode;
35873     },
35874
35875     isChecked : function(){
35876         return this.checkbox ? this.checkbox.checked : false;
35877     },
35878
35879     updateExpandIcon : function(){
35880         if(this.rendered){
35881             var n = this.node, c1, c2;
35882             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35883             var hasChild = n.hasChildNodes();
35884             if(hasChild){
35885                 if(n.expanded){
35886                     cls += "-minus";
35887                     c1 = "x-tree-node-collapsed";
35888                     c2 = "x-tree-node-expanded";
35889                 }else{
35890                     cls += "-plus";
35891                     c1 = "x-tree-node-expanded";
35892                     c2 = "x-tree-node-collapsed";
35893                 }
35894                 if(this.wasLeaf){
35895                     this.removeClass("x-tree-node-leaf");
35896                     this.wasLeaf = false;
35897                 }
35898                 if(this.c1 != c1 || this.c2 != c2){
35899                     Roo.fly(this.elNode).replaceClass(c1, c2);
35900                     this.c1 = c1; this.c2 = c2;
35901                 }
35902             }else{
35903                 // this changes non-leafs into leafs if they have no children.
35904                 // it's not very rational behaviour..
35905                 
35906                 if(!this.wasLeaf && this.node.leaf){
35907                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35908                     delete this.c1;
35909                     delete this.c2;
35910                     this.wasLeaf = true;
35911                 }
35912             }
35913             var ecc = "x-tree-ec-icon "+cls;
35914             if(this.ecc != ecc){
35915                 this.ecNode.className = ecc;
35916                 this.ecc = ecc;
35917             }
35918         }
35919     },
35920
35921     getChildIndent : function(){
35922         if(!this.childIndent){
35923             var buf = [];
35924             var p = this.node;
35925             while(p){
35926                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35927                     if(!p.isLast()) {
35928                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35929                     } else {
35930                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35931                     }
35932                 }
35933                 p = p.parentNode;
35934             }
35935             this.childIndent = buf.join("");
35936         }
35937         return this.childIndent;
35938     },
35939
35940     renderIndent : function(){
35941         if(this.rendered){
35942             var indent = "";
35943             var p = this.node.parentNode;
35944             if(p){
35945                 indent = p.ui.getChildIndent();
35946             }
35947             if(this.indentMarkup != indent){ // don't rerender if not required
35948                 this.indentNode.innerHTML = indent;
35949                 this.indentMarkup = indent;
35950             }
35951             this.updateExpandIcon();
35952         }
35953     }
35954 };
35955
35956 Roo.tree.RootTreeNodeUI = function(){
35957     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35958 };
35959 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35960     render : function(){
35961         if(!this.rendered){
35962             var targetNode = this.node.ownerTree.innerCt.dom;
35963             this.node.expanded = true;
35964             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35965             this.wrap = this.ctNode = targetNode.firstChild;
35966         }
35967     },
35968     collapse : function(){
35969     },
35970     expand : function(){
35971     }
35972 });/*
35973  * Based on:
35974  * Ext JS Library 1.1.1
35975  * Copyright(c) 2006-2007, Ext JS, LLC.
35976  *
35977  * Originally Released Under LGPL - original licence link has changed is not relivant.
35978  *
35979  * Fork - LGPL
35980  * <script type="text/javascript">
35981  */
35982 /**
35983  * @class Roo.tree.TreeLoader
35984  * @extends Roo.util.Observable
35985  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
35986  * nodes from a specified URL. The response must be a javascript Array definition
35987  * who's elements are node definition objects. eg:
35988  * <pre><code>
35989 {  success : true,
35990    data :      [
35991    
35992     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
35993     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
35994     ]
35995 }
35996
35997
35998 </code></pre>
35999  * <br><br>
36000  * The old style respose with just an array is still supported, but not recommended.
36001  * <br><br>
36002  *
36003  * A server request is sent, and child nodes are loaded only when a node is expanded.
36004  * The loading node's id is passed to the server under the parameter name "node" to
36005  * enable the server to produce the correct child nodes.
36006  * <br><br>
36007  * To pass extra parameters, an event handler may be attached to the "beforeload"
36008  * event, and the parameters specified in the TreeLoader's baseParams property:
36009  * <pre><code>
36010     myTreeLoader.on("beforeload", function(treeLoader, node) {
36011         this.baseParams.category = node.attributes.category;
36012     }, this);
36013 </code></pre><
36014  * This would pass an HTTP parameter called "category" to the server containing
36015  * the value of the Node's "category" attribute.
36016  * @constructor
36017  * Creates a new Treeloader.
36018  * @param {Object} config A config object containing config properties.
36019  */
36020 Roo.tree.TreeLoader = function(config){
36021     this.baseParams = {};
36022     this.requestMethod = "POST";
36023     Roo.apply(this, config);
36024
36025     this.addEvents({
36026     
36027         /**
36028          * @event beforeload
36029          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36030          * @param {Object} This TreeLoader object.
36031          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36032          * @param {Object} callback The callback function specified in the {@link #load} call.
36033          */
36034         beforeload : true,
36035         /**
36036          * @event load
36037          * Fires when the node has been successfuly loaded.
36038          * @param {Object} This TreeLoader object.
36039          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36040          * @param {Object} response The response object containing the data from the server.
36041          */
36042         load : true,
36043         /**
36044          * @event loadexception
36045          * Fires if the network request failed.
36046          * @param {Object} This TreeLoader object.
36047          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36048          * @param {Object} response The response object containing the data from the server.
36049          */
36050         loadexception : true,
36051         /**
36052          * @event create
36053          * Fires before a node is created, enabling you to return custom Node types 
36054          * @param {Object} This TreeLoader object.
36055          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36056          */
36057         create : true
36058     });
36059
36060     Roo.tree.TreeLoader.superclass.constructor.call(this);
36061 };
36062
36063 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36064     /**
36065     * @cfg {String} dataUrl The URL from which to request a Json string which
36066     * specifies an array of node definition object representing the child nodes
36067     * to be loaded.
36068     */
36069     /**
36070     * @cfg {String} requestMethod either GET or POST
36071     * defaults to POST (due to BC)
36072     * to be loaded.
36073     */
36074     /**
36075     * @cfg {Object} baseParams (optional) An object containing properties which
36076     * specify HTTP parameters to be passed to each request for child nodes.
36077     */
36078     /**
36079     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36080     * created by this loader. If the attributes sent by the server have an attribute in this object,
36081     * they take priority.
36082     */
36083     /**
36084     * @cfg {Object} uiProviders (optional) An object containing properties which
36085     * 
36086     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36087     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36088     * <i>uiProvider</i> attribute of a returned child node is a string rather
36089     * than a reference to a TreeNodeUI implementation, this that string value
36090     * is used as a property name in the uiProviders object. You can define the provider named
36091     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36092     */
36093     uiProviders : {},
36094
36095     /**
36096     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36097     * child nodes before loading.
36098     */
36099     clearOnLoad : true,
36100
36101     /**
36102     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36103     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36104     * Grid query { data : [ .....] }
36105     */
36106     
36107     root : false,
36108      /**
36109     * @cfg {String} queryParam (optional) 
36110     * Name of the query as it will be passed on the querystring (defaults to 'node')
36111     * eg. the request will be ?node=[id]
36112     */
36113     
36114     
36115     queryParam: false,
36116     
36117     /**
36118      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36119      * This is called automatically when a node is expanded, but may be used to reload
36120      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36121      * @param {Roo.tree.TreeNode} node
36122      * @param {Function} callback
36123      */
36124     load : function(node, callback){
36125         if(this.clearOnLoad){
36126             while(node.firstChild){
36127                 node.removeChild(node.firstChild);
36128             }
36129         }
36130         if(node.attributes.children){ // preloaded json children
36131             var cs = node.attributes.children;
36132             for(var i = 0, len = cs.length; i < len; i++){
36133                 node.appendChild(this.createNode(cs[i]));
36134             }
36135             if(typeof callback == "function"){
36136                 callback();
36137             }
36138         }else if(this.dataUrl){
36139             this.requestData(node, callback);
36140         }
36141     },
36142
36143     getParams: function(node){
36144         var buf = [], bp = this.baseParams;
36145         for(var key in bp){
36146             if(typeof bp[key] != "function"){
36147                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36148             }
36149         }
36150         var n = this.queryParam === false ? 'node' : this.queryParam;
36151         buf.push(n + "=", encodeURIComponent(node.id));
36152         return buf.join("");
36153     },
36154
36155     requestData : function(node, callback){
36156         if(this.fireEvent("beforeload", this, node, callback) !== false){
36157             this.transId = Roo.Ajax.request({
36158                 method:this.requestMethod,
36159                 url: this.dataUrl||this.url,
36160                 success: this.handleResponse,
36161                 failure: this.handleFailure,
36162                 scope: this,
36163                 argument: {callback: callback, node: node},
36164                 params: this.getParams(node)
36165             });
36166         }else{
36167             // if the load is cancelled, make sure we notify
36168             // the node that we are done
36169             if(typeof callback == "function"){
36170                 callback();
36171             }
36172         }
36173     },
36174
36175     isLoading : function(){
36176         return this.transId ? true : false;
36177     },
36178
36179     abort : function(){
36180         if(this.isLoading()){
36181             Roo.Ajax.abort(this.transId);
36182         }
36183     },
36184
36185     // private
36186     createNode : function(attr)
36187     {
36188         // apply baseAttrs, nice idea Corey!
36189         if(this.baseAttrs){
36190             Roo.applyIf(attr, this.baseAttrs);
36191         }
36192         if(this.applyLoader !== false){
36193             attr.loader = this;
36194         }
36195         // uiProvider = depreciated..
36196         
36197         if(typeof(attr.uiProvider) == 'string'){
36198            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36199                 /**  eval:var:attr */ eval(attr.uiProvider);
36200         }
36201         if(typeof(this.uiProviders['default']) != 'undefined') {
36202             attr.uiProvider = this.uiProviders['default'];
36203         }
36204         
36205         this.fireEvent('create', this, attr);
36206         
36207         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36208         return(attr.leaf ?
36209                         new Roo.tree.TreeNode(attr) :
36210                         new Roo.tree.AsyncTreeNode(attr));
36211     },
36212
36213     processResponse : function(response, node, callback)
36214     {
36215         var json = response.responseText;
36216         try {
36217             
36218             var o = Roo.decode(json);
36219             
36220             if (this.root === false && typeof(o.success) != undefined) {
36221                 this.root = 'data'; // the default behaviour for list like data..
36222                 }
36223                 
36224             if (this.root !== false &&  !o.success) {
36225                 // it's a failure condition.
36226                 var a = response.argument;
36227                 this.fireEvent("loadexception", this, a.node, response);
36228                 Roo.log("Load failed - should have a handler really");
36229                 return;
36230             }
36231             
36232             
36233             
36234             if (this.root !== false) {
36235                  o = o[this.root];
36236             }
36237             
36238             for(var i = 0, len = o.length; i < len; i++){
36239                 var n = this.createNode(o[i]);
36240                 if(n){
36241                     node.appendChild(n);
36242                 }
36243             }
36244             if(typeof callback == "function"){
36245                 callback(this, node);
36246             }
36247         }catch(e){
36248             this.handleFailure(response);
36249         }
36250     },
36251
36252     handleResponse : function(response){
36253         this.transId = false;
36254         var a = response.argument;
36255         this.processResponse(response, a.node, a.callback);
36256         this.fireEvent("load", this, a.node, response);
36257     },
36258
36259     handleFailure : function(response)
36260     {
36261         // should handle failure better..
36262         this.transId = false;
36263         var a = response.argument;
36264         this.fireEvent("loadexception", this, a.node, response);
36265         if(typeof a.callback == "function"){
36266             a.callback(this, a.node);
36267         }
36268     }
36269 });/*
36270  * Based on:
36271  * Ext JS Library 1.1.1
36272  * Copyright(c) 2006-2007, Ext JS, LLC.
36273  *
36274  * Originally Released Under LGPL - original licence link has changed is not relivant.
36275  *
36276  * Fork - LGPL
36277  * <script type="text/javascript">
36278  */
36279
36280 /**
36281 * @class Roo.tree.TreeFilter
36282 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36283 * @param {TreePanel} tree
36284 * @param {Object} config (optional)
36285  */
36286 Roo.tree.TreeFilter = function(tree, config){
36287     this.tree = tree;
36288     this.filtered = {};
36289     Roo.apply(this, config);
36290 };
36291
36292 Roo.tree.TreeFilter.prototype = {
36293     clearBlank:false,
36294     reverse:false,
36295     autoClear:false,
36296     remove:false,
36297
36298      /**
36299      * Filter the data by a specific attribute.
36300      * @param {String/RegExp} value Either string that the attribute value
36301      * should start with or a RegExp to test against the attribute
36302      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36303      * @param {TreeNode} startNode (optional) The node to start the filter at.
36304      */
36305     filter : function(value, attr, startNode){
36306         attr = attr || "text";
36307         var f;
36308         if(typeof value == "string"){
36309             var vlen = value.length;
36310             // auto clear empty filter
36311             if(vlen == 0 && this.clearBlank){
36312                 this.clear();
36313                 return;
36314             }
36315             value = value.toLowerCase();
36316             f = function(n){
36317                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36318             };
36319         }else if(value.exec){ // regex?
36320             f = function(n){
36321                 return value.test(n.attributes[attr]);
36322             };
36323         }else{
36324             throw 'Illegal filter type, must be string or regex';
36325         }
36326         this.filterBy(f, null, startNode);
36327         },
36328
36329     /**
36330      * Filter by a function. The passed function will be called with each
36331      * node in the tree (or from the startNode). If the function returns true, the node is kept
36332      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36333      * @param {Function} fn The filter function
36334      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36335      */
36336     filterBy : function(fn, scope, startNode){
36337         startNode = startNode || this.tree.root;
36338         if(this.autoClear){
36339             this.clear();
36340         }
36341         var af = this.filtered, rv = this.reverse;
36342         var f = function(n){
36343             if(n == startNode){
36344                 return true;
36345             }
36346             if(af[n.id]){
36347                 return false;
36348             }
36349             var m = fn.call(scope || n, n);
36350             if(!m || rv){
36351                 af[n.id] = n;
36352                 n.ui.hide();
36353                 return false;
36354             }
36355             return true;
36356         };
36357         startNode.cascade(f);
36358         if(this.remove){
36359            for(var id in af){
36360                if(typeof id != "function"){
36361                    var n = af[id];
36362                    if(n && n.parentNode){
36363                        n.parentNode.removeChild(n);
36364                    }
36365                }
36366            }
36367         }
36368     },
36369
36370     /**
36371      * Clears the current filter. Note: with the "remove" option
36372      * set a filter cannot be cleared.
36373      */
36374     clear : function(){
36375         var t = this.tree;
36376         var af = this.filtered;
36377         for(var id in af){
36378             if(typeof id != "function"){
36379                 var n = af[id];
36380                 if(n){
36381                     n.ui.show();
36382                 }
36383             }
36384         }
36385         this.filtered = {};
36386     }
36387 };
36388 /*
36389  * Based on:
36390  * Ext JS Library 1.1.1
36391  * Copyright(c) 2006-2007, Ext JS, LLC.
36392  *
36393  * Originally Released Under LGPL - original licence link has changed is not relivant.
36394  *
36395  * Fork - LGPL
36396  * <script type="text/javascript">
36397  */
36398  
36399
36400 /**
36401  * @class Roo.tree.TreeSorter
36402  * Provides sorting of nodes in a TreePanel
36403  * 
36404  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36405  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36406  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36407  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36408  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36409  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36410  * @constructor
36411  * @param {TreePanel} tree
36412  * @param {Object} config
36413  */
36414 Roo.tree.TreeSorter = function(tree, config){
36415     Roo.apply(this, config);
36416     tree.on("beforechildrenrendered", this.doSort, this);
36417     tree.on("append", this.updateSort, this);
36418     tree.on("insert", this.updateSort, this);
36419     
36420     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36421     var p = this.property || "text";
36422     var sortType = this.sortType;
36423     var fs = this.folderSort;
36424     var cs = this.caseSensitive === true;
36425     var leafAttr = this.leafAttr || 'leaf';
36426
36427     this.sortFn = function(n1, n2){
36428         if(fs){
36429             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36430                 return 1;
36431             }
36432             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36433                 return -1;
36434             }
36435         }
36436         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36437         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36438         if(v1 < v2){
36439                         return dsc ? +1 : -1;
36440                 }else if(v1 > v2){
36441                         return dsc ? -1 : +1;
36442         }else{
36443                 return 0;
36444         }
36445     };
36446 };
36447
36448 Roo.tree.TreeSorter.prototype = {
36449     doSort : function(node){
36450         node.sort(this.sortFn);
36451     },
36452     
36453     compareNodes : function(n1, n2){
36454         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36455     },
36456     
36457     updateSort : function(tree, node){
36458         if(node.childrenRendered){
36459             this.doSort.defer(1, this, [node]);
36460         }
36461     }
36462 };/*
36463  * Based on:
36464  * Ext JS Library 1.1.1
36465  * Copyright(c) 2006-2007, Ext JS, LLC.
36466  *
36467  * Originally Released Under LGPL - original licence link has changed is not relivant.
36468  *
36469  * Fork - LGPL
36470  * <script type="text/javascript">
36471  */
36472
36473 if(Roo.dd.DropZone){
36474     
36475 Roo.tree.TreeDropZone = function(tree, config){
36476     this.allowParentInsert = false;
36477     this.allowContainerDrop = false;
36478     this.appendOnly = false;
36479     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36480     this.tree = tree;
36481     this.lastInsertClass = "x-tree-no-status";
36482     this.dragOverData = {};
36483 };
36484
36485 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36486     ddGroup : "TreeDD",
36487     scroll:  true,
36488     
36489     expandDelay : 1000,
36490     
36491     expandNode : function(node){
36492         if(node.hasChildNodes() && !node.isExpanded()){
36493             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36494         }
36495     },
36496     
36497     queueExpand : function(node){
36498         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36499     },
36500     
36501     cancelExpand : function(){
36502         if(this.expandProcId){
36503             clearTimeout(this.expandProcId);
36504             this.expandProcId = false;
36505         }
36506     },
36507     
36508     isValidDropPoint : function(n, pt, dd, e, data){
36509         if(!n || !data){ return false; }
36510         var targetNode = n.node;
36511         var dropNode = data.node;
36512         // default drop rules
36513         if(!(targetNode && targetNode.isTarget && pt)){
36514             return false;
36515         }
36516         if(pt == "append" && targetNode.allowChildren === false){
36517             return false;
36518         }
36519         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36520             return false;
36521         }
36522         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36523             return false;
36524         }
36525         // reuse the object
36526         var overEvent = this.dragOverData;
36527         overEvent.tree = this.tree;
36528         overEvent.target = targetNode;
36529         overEvent.data = data;
36530         overEvent.point = pt;
36531         overEvent.source = dd;
36532         overEvent.rawEvent = e;
36533         overEvent.dropNode = dropNode;
36534         overEvent.cancel = false;  
36535         var result = this.tree.fireEvent("nodedragover", overEvent);
36536         return overEvent.cancel === false && result !== false;
36537     },
36538     
36539     getDropPoint : function(e, n, dd)
36540     {
36541         var tn = n.node;
36542         if(tn.isRoot){
36543             return tn.allowChildren !== false ? "append" : false; // always append for root
36544         }
36545         var dragEl = n.ddel;
36546         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36547         var y = Roo.lib.Event.getPageY(e);
36548         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36549         
36550         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36551         var noAppend = tn.allowChildren === false;
36552         if(this.appendOnly || tn.parentNode.allowChildren === false){
36553             return noAppend ? false : "append";
36554         }
36555         var noBelow = false;
36556         if(!this.allowParentInsert){
36557             noBelow = tn.hasChildNodes() && tn.isExpanded();
36558         }
36559         var q = (b - t) / (noAppend ? 2 : 3);
36560         if(y >= t && y < (t + q)){
36561             return "above";
36562         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36563             return "below";
36564         }else{
36565             return "append";
36566         }
36567     },
36568     
36569     onNodeEnter : function(n, dd, e, data)
36570     {
36571         this.cancelExpand();
36572     },
36573     
36574     onNodeOver : function(n, dd, e, data)
36575     {
36576        
36577         var pt = this.getDropPoint(e, n, dd);
36578         var node = n.node;
36579         
36580         // auto node expand check
36581         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36582             this.queueExpand(node);
36583         }else if(pt != "append"){
36584             this.cancelExpand();
36585         }
36586         
36587         // set the insert point style on the target node
36588         var returnCls = this.dropNotAllowed;
36589         if(this.isValidDropPoint(n, pt, dd, e, data)){
36590            if(pt){
36591                var el = n.ddel;
36592                var cls;
36593                if(pt == "above"){
36594                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36595                    cls = "x-tree-drag-insert-above";
36596                }else if(pt == "below"){
36597                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36598                    cls = "x-tree-drag-insert-below";
36599                }else{
36600                    returnCls = "x-tree-drop-ok-append";
36601                    cls = "x-tree-drag-append";
36602                }
36603                if(this.lastInsertClass != cls){
36604                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36605                    this.lastInsertClass = cls;
36606                }
36607            }
36608        }
36609        return returnCls;
36610     },
36611     
36612     onNodeOut : function(n, dd, e, data){
36613         
36614         this.cancelExpand();
36615         this.removeDropIndicators(n);
36616     },
36617     
36618     onNodeDrop : function(n, dd, e, data){
36619         var point = this.getDropPoint(e, n, dd);
36620         var targetNode = n.node;
36621         targetNode.ui.startDrop();
36622         if(!this.isValidDropPoint(n, point, dd, e, data)){
36623             targetNode.ui.endDrop();
36624             return false;
36625         }
36626         // first try to find the drop node
36627         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36628         var dropEvent = {
36629             tree : this.tree,
36630             target: targetNode,
36631             data: data,
36632             point: point,
36633             source: dd,
36634             rawEvent: e,
36635             dropNode: dropNode,
36636             cancel: !dropNode   
36637         };
36638         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36639         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36640             targetNode.ui.endDrop();
36641             return false;
36642         }
36643         // allow target changing
36644         targetNode = dropEvent.target;
36645         if(point == "append" && !targetNode.isExpanded()){
36646             targetNode.expand(false, null, function(){
36647                 this.completeDrop(dropEvent);
36648             }.createDelegate(this));
36649         }else{
36650             this.completeDrop(dropEvent);
36651         }
36652         return true;
36653     },
36654     
36655     completeDrop : function(de){
36656         var ns = de.dropNode, p = de.point, t = de.target;
36657         if(!(ns instanceof Array)){
36658             ns = [ns];
36659         }
36660         var n;
36661         for(var i = 0, len = ns.length; i < len; i++){
36662             n = ns[i];
36663             if(p == "above"){
36664                 t.parentNode.insertBefore(n, t);
36665             }else if(p == "below"){
36666                 t.parentNode.insertBefore(n, t.nextSibling);
36667             }else{
36668                 t.appendChild(n);
36669             }
36670         }
36671         n.ui.focus();
36672         if(this.tree.hlDrop){
36673             n.ui.highlight();
36674         }
36675         t.ui.endDrop();
36676         this.tree.fireEvent("nodedrop", de);
36677     },
36678     
36679     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36680         if(this.tree.hlDrop){
36681             dropNode.ui.focus();
36682             dropNode.ui.highlight();
36683         }
36684         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36685     },
36686     
36687     getTree : function(){
36688         return this.tree;
36689     },
36690     
36691     removeDropIndicators : function(n){
36692         if(n && n.ddel){
36693             var el = n.ddel;
36694             Roo.fly(el).removeClass([
36695                     "x-tree-drag-insert-above",
36696                     "x-tree-drag-insert-below",
36697                     "x-tree-drag-append"]);
36698             this.lastInsertClass = "_noclass";
36699         }
36700     },
36701     
36702     beforeDragDrop : function(target, e, id){
36703         this.cancelExpand();
36704         return true;
36705     },
36706     
36707     afterRepair : function(data){
36708         if(data && Roo.enableFx){
36709             data.node.ui.highlight();
36710         }
36711         this.hideProxy();
36712     } 
36713     
36714 });
36715
36716 }
36717 /*
36718  * Based on:
36719  * Ext JS Library 1.1.1
36720  * Copyright(c) 2006-2007, Ext JS, LLC.
36721  *
36722  * Originally Released Under LGPL - original licence link has changed is not relivant.
36723  *
36724  * Fork - LGPL
36725  * <script type="text/javascript">
36726  */
36727  
36728
36729 if(Roo.dd.DragZone){
36730 Roo.tree.TreeDragZone = function(tree, config){
36731     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36732     this.tree = tree;
36733 };
36734
36735 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36736     ddGroup : "TreeDD",
36737    
36738     onBeforeDrag : function(data, e){
36739         var n = data.node;
36740         return n && n.draggable && !n.disabled;
36741     },
36742      
36743     
36744     onInitDrag : function(e){
36745         var data = this.dragData;
36746         this.tree.getSelectionModel().select(data.node);
36747         this.proxy.update("");
36748         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36749         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36750     },
36751     
36752     getRepairXY : function(e, data){
36753         return data.node.ui.getDDRepairXY();
36754     },
36755     
36756     onEndDrag : function(data, e){
36757         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36758         
36759         
36760     },
36761     
36762     onValidDrop : function(dd, e, id){
36763         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36764         this.hideProxy();
36765     },
36766     
36767     beforeInvalidDrop : function(e, id){
36768         // this scrolls the original position back into view
36769         var sm = this.tree.getSelectionModel();
36770         sm.clearSelections();
36771         sm.select(this.dragData.node);
36772     }
36773 });
36774 }/*
36775  * Based on:
36776  * Ext JS Library 1.1.1
36777  * Copyright(c) 2006-2007, Ext JS, LLC.
36778  *
36779  * Originally Released Under LGPL - original licence link has changed is not relivant.
36780  *
36781  * Fork - LGPL
36782  * <script type="text/javascript">
36783  */
36784 /**
36785  * @class Roo.tree.TreeEditor
36786  * @extends Roo.Editor
36787  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36788  * as the editor field.
36789  * @constructor
36790  * @param {Object} config (used to be the tree panel.)
36791  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36792  * 
36793  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36794  * @cfg {Roo.form.TextField|Object} field The field configuration
36795  *
36796  * 
36797  */
36798 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36799     var tree = config;
36800     var field;
36801     if (oldconfig) { // old style..
36802         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36803     } else {
36804         // new style..
36805         tree = config.tree;
36806         config.field = config.field  || {};
36807         config.field.xtype = 'TextField';
36808         field = Roo.factory(config.field, Roo.form);
36809     }
36810     config = config || {};
36811     
36812     
36813     this.addEvents({
36814         /**
36815          * @event beforenodeedit
36816          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36817          * false from the handler of this event.
36818          * @param {Editor} this
36819          * @param {Roo.tree.Node} node 
36820          */
36821         "beforenodeedit" : true
36822     });
36823     
36824     //Roo.log(config);
36825     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36826
36827     this.tree = tree;
36828
36829     tree.on('beforeclick', this.beforeNodeClick, this);
36830     tree.getTreeEl().on('mousedown', this.hide, this);
36831     this.on('complete', this.updateNode, this);
36832     this.on('beforestartedit', this.fitToTree, this);
36833     this.on('startedit', this.bindScroll, this, {delay:10});
36834     this.on('specialkey', this.onSpecialKey, this);
36835 };
36836
36837 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36838     /**
36839      * @cfg {String} alignment
36840      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36841      */
36842     alignment: "l-l",
36843     // inherit
36844     autoSize: false,
36845     /**
36846      * @cfg {Boolean} hideEl
36847      * True to hide the bound element while the editor is displayed (defaults to false)
36848      */
36849     hideEl : false,
36850     /**
36851      * @cfg {String} cls
36852      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36853      */
36854     cls: "x-small-editor x-tree-editor",
36855     /**
36856      * @cfg {Boolean} shim
36857      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36858      */
36859     shim:false,
36860     // inherit
36861     shadow:"frame",
36862     /**
36863      * @cfg {Number} maxWidth
36864      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36865      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36866      * scroll and client offsets into account prior to each edit.
36867      */
36868     maxWidth: 250,
36869
36870     editDelay : 350,
36871
36872     // private
36873     fitToTree : function(ed, el){
36874         var td = this.tree.getTreeEl().dom, nd = el.dom;
36875         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36876             td.scrollLeft = nd.offsetLeft;
36877         }
36878         var w = Math.min(
36879                 this.maxWidth,
36880                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36881         this.setSize(w, '');
36882         
36883         return this.fireEvent('beforenodeedit', this, this.editNode);
36884         
36885     },
36886
36887     // private
36888     triggerEdit : function(node){
36889         this.completeEdit();
36890         this.editNode = node;
36891         this.startEdit(node.ui.textNode, node.text);
36892     },
36893
36894     // private
36895     bindScroll : function(){
36896         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36897     },
36898
36899     // private
36900     beforeNodeClick : function(node, e){
36901         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36902         this.lastClick = new Date();
36903         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36904             e.stopEvent();
36905             this.triggerEdit(node);
36906             return false;
36907         }
36908         return true;
36909     },
36910
36911     // private
36912     updateNode : function(ed, value){
36913         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36914         this.editNode.setText(value);
36915     },
36916
36917     // private
36918     onHide : function(){
36919         Roo.tree.TreeEditor.superclass.onHide.call(this);
36920         if(this.editNode){
36921             this.editNode.ui.focus();
36922         }
36923     },
36924
36925     // private
36926     onSpecialKey : function(field, e){
36927         var k = e.getKey();
36928         if(k == e.ESC){
36929             e.stopEvent();
36930             this.cancelEdit();
36931         }else if(k == e.ENTER && !e.hasModifier()){
36932             e.stopEvent();
36933             this.completeEdit();
36934         }
36935     }
36936 });//<Script type="text/javascript">
36937 /*
36938  * Based on:
36939  * Ext JS Library 1.1.1
36940  * Copyright(c) 2006-2007, Ext JS, LLC.
36941  *
36942  * Originally Released Under LGPL - original licence link has changed is not relivant.
36943  *
36944  * Fork - LGPL
36945  * <script type="text/javascript">
36946  */
36947  
36948 /**
36949  * Not documented??? - probably should be...
36950  */
36951
36952 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36953     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36954     
36955     renderElements : function(n, a, targetNode, bulkRender){
36956         //consel.log("renderElements?");
36957         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36958
36959         var t = n.getOwnerTree();
36960         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36961         
36962         var cols = t.columns;
36963         var bw = t.borderWidth;
36964         var c = cols[0];
36965         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36966          var cb = typeof a.checked == "boolean";
36967         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36968         var colcls = 'x-t-' + tid + '-c0';
36969         var buf = [
36970             '<li class="x-tree-node">',
36971             
36972                 
36973                 '<div class="x-tree-node-el ', a.cls,'">',
36974                     // extran...
36975                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
36976                 
36977                 
36978                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
36979                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
36980                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
36981                            (a.icon ? ' x-tree-node-inline-icon' : ''),
36982                            (a.iconCls ? ' '+a.iconCls : ''),
36983                            '" unselectable="on" />',
36984                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
36985                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
36986                              
36987                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36988                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
36989                             '<span unselectable="on" qtip="' + tx + '">',
36990                              tx,
36991                              '</span></a>' ,
36992                     '</div>',
36993                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36994                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
36995                  ];
36996         for(var i = 1, len = cols.length; i < len; i++){
36997             c = cols[i];
36998             colcls = 'x-t-' + tid + '-c' +i;
36999             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37000             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37001                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37002                       "</div>");
37003          }
37004          
37005          buf.push(
37006             '</a>',
37007             '<div class="x-clear"></div></div>',
37008             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37009             "</li>");
37010         
37011         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37012             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37013                                 n.nextSibling.ui.getEl(), buf.join(""));
37014         }else{
37015             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37016         }
37017         var el = this.wrap.firstChild;
37018         this.elRow = el;
37019         this.elNode = el.firstChild;
37020         this.ranchor = el.childNodes[1];
37021         this.ctNode = this.wrap.childNodes[1];
37022         var cs = el.firstChild.childNodes;
37023         this.indentNode = cs[0];
37024         this.ecNode = cs[1];
37025         this.iconNode = cs[2];
37026         var index = 3;
37027         if(cb){
37028             this.checkbox = cs[3];
37029             index++;
37030         }
37031         this.anchor = cs[index];
37032         
37033         this.textNode = cs[index].firstChild;
37034         
37035         //el.on("click", this.onClick, this);
37036         //el.on("dblclick", this.onDblClick, this);
37037         
37038         
37039        // console.log(this);
37040     },
37041     initEvents : function(){
37042         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37043         
37044             
37045         var a = this.ranchor;
37046
37047         var el = Roo.get(a);
37048
37049         if(Roo.isOpera){ // opera render bug ignores the CSS
37050             el.setStyle("text-decoration", "none");
37051         }
37052
37053         el.on("click", this.onClick, this);
37054         el.on("dblclick", this.onDblClick, this);
37055         el.on("contextmenu", this.onContextMenu, this);
37056         
37057     },
37058     
37059     /*onSelectedChange : function(state){
37060         if(state){
37061             this.focus();
37062             this.addClass("x-tree-selected");
37063         }else{
37064             //this.blur();
37065             this.removeClass("x-tree-selected");
37066         }
37067     },*/
37068     addClass : function(cls){
37069         if(this.elRow){
37070             Roo.fly(this.elRow).addClass(cls);
37071         }
37072         
37073     },
37074     
37075     
37076     removeClass : function(cls){
37077         if(this.elRow){
37078             Roo.fly(this.elRow).removeClass(cls);
37079         }
37080     }
37081
37082     
37083     
37084 });//<Script type="text/javascript">
37085
37086 /*
37087  * Based on:
37088  * Ext JS Library 1.1.1
37089  * Copyright(c) 2006-2007, Ext JS, LLC.
37090  *
37091  * Originally Released Under LGPL - original licence link has changed is not relivant.
37092  *
37093  * Fork - LGPL
37094  * <script type="text/javascript">
37095  */
37096  
37097
37098 /**
37099  * @class Roo.tree.ColumnTree
37100  * @extends Roo.data.TreePanel
37101  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37102  * @cfg {int} borderWidth  compined right/left border allowance
37103  * @constructor
37104  * @param {String/HTMLElement/Element} el The container element
37105  * @param {Object} config
37106  */
37107 Roo.tree.ColumnTree =  function(el, config)
37108 {
37109    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37110    this.addEvents({
37111         /**
37112         * @event resize
37113         * Fire this event on a container when it resizes
37114         * @param {int} w Width
37115         * @param {int} h Height
37116         */
37117        "resize" : true
37118     });
37119     this.on('resize', this.onResize, this);
37120 };
37121
37122 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37123     //lines:false,
37124     
37125     
37126     borderWidth: Roo.isBorderBox ? 0 : 2, 
37127     headEls : false,
37128     
37129     render : function(){
37130         // add the header.....
37131        
37132         Roo.tree.ColumnTree.superclass.render.apply(this);
37133         
37134         this.el.addClass('x-column-tree');
37135         
37136         this.headers = this.el.createChild(
37137             {cls:'x-tree-headers'},this.innerCt.dom);
37138    
37139         var cols = this.columns, c;
37140         var totalWidth = 0;
37141         this.headEls = [];
37142         var  len = cols.length;
37143         for(var i = 0; i < len; i++){
37144              c = cols[i];
37145              totalWidth += c.width;
37146             this.headEls.push(this.headers.createChild({
37147                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37148                  cn: {
37149                      cls:'x-tree-hd-text',
37150                      html: c.header
37151                  },
37152                  style:'width:'+(c.width-this.borderWidth)+'px;'
37153              }));
37154         }
37155         this.headers.createChild({cls:'x-clear'});
37156         // prevent floats from wrapping when clipped
37157         this.headers.setWidth(totalWidth);
37158         //this.innerCt.setWidth(totalWidth);
37159         this.innerCt.setStyle({ overflow: 'auto' });
37160         this.onResize(this.width, this.height);
37161              
37162         
37163     },
37164     onResize : function(w,h)
37165     {
37166         this.height = h;
37167         this.width = w;
37168         // resize cols..
37169         this.innerCt.setWidth(this.width);
37170         this.innerCt.setHeight(this.height-20);
37171         
37172         // headers...
37173         var cols = this.columns, c;
37174         var totalWidth = 0;
37175         var expEl = false;
37176         var len = cols.length;
37177         for(var i = 0; i < len; i++){
37178             c = cols[i];
37179             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37180                 // it's the expander..
37181                 expEl  = this.headEls[i];
37182                 continue;
37183             }
37184             totalWidth += c.width;
37185             
37186         }
37187         if (expEl) {
37188             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37189         }
37190         this.headers.setWidth(w-20);
37191
37192         
37193         
37194         
37195     }
37196 });
37197 /*
37198  * Based on:
37199  * Ext JS Library 1.1.1
37200  * Copyright(c) 2006-2007, Ext JS, LLC.
37201  *
37202  * Originally Released Under LGPL - original licence link has changed is not relivant.
37203  *
37204  * Fork - LGPL
37205  * <script type="text/javascript">
37206  */
37207  
37208 /**
37209  * @class Roo.menu.Menu
37210  * @extends Roo.util.Observable
37211  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37212  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37213  * @constructor
37214  * Creates a new Menu
37215  * @param {Object} config Configuration options
37216  */
37217 Roo.menu.Menu = function(config){
37218     Roo.apply(this, config);
37219     this.id = this.id || Roo.id();
37220     this.addEvents({
37221         /**
37222          * @event beforeshow
37223          * Fires before this menu is displayed
37224          * @param {Roo.menu.Menu} this
37225          */
37226         beforeshow : true,
37227         /**
37228          * @event beforehide
37229          * Fires before this menu is hidden
37230          * @param {Roo.menu.Menu} this
37231          */
37232         beforehide : true,
37233         /**
37234          * @event show
37235          * Fires after this menu is displayed
37236          * @param {Roo.menu.Menu} this
37237          */
37238         show : true,
37239         /**
37240          * @event hide
37241          * Fires after this menu is hidden
37242          * @param {Roo.menu.Menu} this
37243          */
37244         hide : true,
37245         /**
37246          * @event click
37247          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37248          * @param {Roo.menu.Menu} this
37249          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37250          * @param {Roo.EventObject} e
37251          */
37252         click : true,
37253         /**
37254          * @event mouseover
37255          * Fires when the mouse is hovering over this menu
37256          * @param {Roo.menu.Menu} this
37257          * @param {Roo.EventObject} e
37258          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37259          */
37260         mouseover : true,
37261         /**
37262          * @event mouseout
37263          * Fires when the mouse exits this menu
37264          * @param {Roo.menu.Menu} this
37265          * @param {Roo.EventObject} e
37266          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37267          */
37268         mouseout : true,
37269         /**
37270          * @event itemclick
37271          * Fires when a menu item contained in this menu is clicked
37272          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37273          * @param {Roo.EventObject} e
37274          */
37275         itemclick: true
37276     });
37277     if (this.registerMenu) {
37278         Roo.menu.MenuMgr.register(this);
37279     }
37280     
37281     var mis = this.items;
37282     this.items = new Roo.util.MixedCollection();
37283     if(mis){
37284         this.add.apply(this, mis);
37285     }
37286 };
37287
37288 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37289     /**
37290      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37291      */
37292     minWidth : 120,
37293     /**
37294      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37295      * for bottom-right shadow (defaults to "sides")
37296      */
37297     shadow : "sides",
37298     /**
37299      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37300      * this menu (defaults to "tl-tr?")
37301      */
37302     subMenuAlign : "tl-tr?",
37303     /**
37304      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37305      * relative to its element of origin (defaults to "tl-bl?")
37306      */
37307     defaultAlign : "tl-bl?",
37308     /**
37309      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37310      */
37311     allowOtherMenus : false,
37312     /**
37313      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37314      */
37315     registerMenu : true,
37316
37317     hidden:true,
37318
37319     // private
37320     render : function(){
37321         if(this.el){
37322             return;
37323         }
37324         var el = this.el = new Roo.Layer({
37325             cls: "x-menu",
37326             shadow:this.shadow,
37327             constrain: false,
37328             parentEl: this.parentEl || document.body,
37329             zindex:15000
37330         });
37331
37332         this.keyNav = new Roo.menu.MenuNav(this);
37333
37334         if(this.plain){
37335             el.addClass("x-menu-plain");
37336         }
37337         if(this.cls){
37338             el.addClass(this.cls);
37339         }
37340         // generic focus element
37341         this.focusEl = el.createChild({
37342             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37343         });
37344         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37345         //disabling touch- as it's causing issues ..
37346         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37347         ul.on('click'   , this.onClick, this);
37348         
37349         
37350         ul.on("mouseover", this.onMouseOver, this);
37351         ul.on("mouseout", this.onMouseOut, this);
37352         this.items.each(function(item){
37353             if (item.hidden) {
37354                 return;
37355             }
37356             
37357             var li = document.createElement("li");
37358             li.className = "x-menu-list-item";
37359             ul.dom.appendChild(li);
37360             item.render(li, this);
37361         }, this);
37362         this.ul = ul;
37363         this.autoWidth();
37364     },
37365
37366     // private
37367     autoWidth : function(){
37368         var el = this.el, ul = this.ul;
37369         if(!el){
37370             return;
37371         }
37372         var w = this.width;
37373         if(w){
37374             el.setWidth(w);
37375         }else if(Roo.isIE){
37376             el.setWidth(this.minWidth);
37377             var t = el.dom.offsetWidth; // force recalc
37378             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37379         }
37380     },
37381
37382     // private
37383     delayAutoWidth : function(){
37384         if(this.rendered){
37385             if(!this.awTask){
37386                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37387             }
37388             this.awTask.delay(20);
37389         }
37390     },
37391
37392     // private
37393     findTargetItem : function(e){
37394         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37395         if(t && t.menuItemId){
37396             return this.items.get(t.menuItemId);
37397         }
37398     },
37399
37400     // private
37401     onClick : function(e){
37402         Roo.log("menu.onClick");
37403         var t = this.findTargetItem(e);
37404         if(!t){
37405             return;
37406         }
37407         Roo.log(e);
37408         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37409             if(t == this.activeItem && t.shouldDeactivate(e)){
37410                 this.activeItem.deactivate();
37411                 delete this.activeItem;
37412                 return;
37413             }
37414             if(t.canActivate){
37415                 this.setActiveItem(t, true);
37416             }
37417             return;
37418             
37419             
37420         }
37421         
37422         t.onClick(e);
37423         this.fireEvent("click", this, t, e);
37424     },
37425
37426     // private
37427     setActiveItem : function(item, autoExpand){
37428         if(item != this.activeItem){
37429             if(this.activeItem){
37430                 this.activeItem.deactivate();
37431             }
37432             this.activeItem = item;
37433             item.activate(autoExpand);
37434         }else if(autoExpand){
37435             item.expandMenu();
37436         }
37437     },
37438
37439     // private
37440     tryActivate : function(start, step){
37441         var items = this.items;
37442         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37443             var item = items.get(i);
37444             if(!item.disabled && item.canActivate){
37445                 this.setActiveItem(item, false);
37446                 return item;
37447             }
37448         }
37449         return false;
37450     },
37451
37452     // private
37453     onMouseOver : function(e){
37454         var t;
37455         if(t = this.findTargetItem(e)){
37456             if(t.canActivate && !t.disabled){
37457                 this.setActiveItem(t, true);
37458             }
37459         }
37460         this.fireEvent("mouseover", this, e, t);
37461     },
37462
37463     // private
37464     onMouseOut : function(e){
37465         var t;
37466         if(t = this.findTargetItem(e)){
37467             if(t == this.activeItem && t.shouldDeactivate(e)){
37468                 this.activeItem.deactivate();
37469                 delete this.activeItem;
37470             }
37471         }
37472         this.fireEvent("mouseout", this, e, t);
37473     },
37474
37475     /**
37476      * Read-only.  Returns true if the menu is currently displayed, else false.
37477      * @type Boolean
37478      */
37479     isVisible : function(){
37480         return this.el && !this.hidden;
37481     },
37482
37483     /**
37484      * Displays this menu relative to another element
37485      * @param {String/HTMLElement/Roo.Element} element The element to align to
37486      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37487      * the element (defaults to this.defaultAlign)
37488      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37489      */
37490     show : function(el, pos, parentMenu){
37491         this.parentMenu = parentMenu;
37492         if(!this.el){
37493             this.render();
37494         }
37495         this.fireEvent("beforeshow", this);
37496         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37497     },
37498
37499     /**
37500      * Displays this menu at a specific xy position
37501      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37502      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37503      */
37504     showAt : function(xy, parentMenu, /* private: */_e){
37505         this.parentMenu = parentMenu;
37506         if(!this.el){
37507             this.render();
37508         }
37509         if(_e !== false){
37510             this.fireEvent("beforeshow", this);
37511             xy = this.el.adjustForConstraints(xy);
37512         }
37513         this.el.setXY(xy);
37514         this.el.show();
37515         this.hidden = false;
37516         this.focus();
37517         this.fireEvent("show", this);
37518     },
37519
37520     focus : function(){
37521         if(!this.hidden){
37522             this.doFocus.defer(50, this);
37523         }
37524     },
37525
37526     doFocus : function(){
37527         if(!this.hidden){
37528             this.focusEl.focus();
37529         }
37530     },
37531
37532     /**
37533      * Hides this menu and optionally all parent menus
37534      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37535      */
37536     hide : function(deep){
37537         if(this.el && this.isVisible()){
37538             this.fireEvent("beforehide", this);
37539             if(this.activeItem){
37540                 this.activeItem.deactivate();
37541                 this.activeItem = null;
37542             }
37543             this.el.hide();
37544             this.hidden = true;
37545             this.fireEvent("hide", this);
37546         }
37547         if(deep === true && this.parentMenu){
37548             this.parentMenu.hide(true);
37549         }
37550     },
37551
37552     /**
37553      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37554      * Any of the following are valid:
37555      * <ul>
37556      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37557      * <li>An HTMLElement object which will be converted to a menu item</li>
37558      * <li>A menu item config object that will be created as a new menu item</li>
37559      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37560      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37561      * </ul>
37562      * Usage:
37563      * <pre><code>
37564 // Create the menu
37565 var menu = new Roo.menu.Menu();
37566
37567 // Create a menu item to add by reference
37568 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37569
37570 // Add a bunch of items at once using different methods.
37571 // Only the last item added will be returned.
37572 var item = menu.add(
37573     menuItem,                // add existing item by ref
37574     'Dynamic Item',          // new TextItem
37575     '-',                     // new separator
37576     { text: 'Config Item' }  // new item by config
37577 );
37578 </code></pre>
37579      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37580      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37581      */
37582     add : function(){
37583         var a = arguments, l = a.length, item;
37584         for(var i = 0; i < l; i++){
37585             var el = a[i];
37586             if ((typeof(el) == "object") && el.xtype && el.xns) {
37587                 el = Roo.factory(el, Roo.menu);
37588             }
37589             
37590             if(el.render){ // some kind of Item
37591                 item = this.addItem(el);
37592             }else if(typeof el == "string"){ // string
37593                 if(el == "separator" || el == "-"){
37594                     item = this.addSeparator();
37595                 }else{
37596                     item = this.addText(el);
37597                 }
37598             }else if(el.tagName || el.el){ // element
37599                 item = this.addElement(el);
37600             }else if(typeof el == "object"){ // must be menu item config?
37601                 item = this.addMenuItem(el);
37602             }
37603         }
37604         return item;
37605     },
37606
37607     /**
37608      * Returns this menu's underlying {@link Roo.Element} object
37609      * @return {Roo.Element} The element
37610      */
37611     getEl : function(){
37612         if(!this.el){
37613             this.render();
37614         }
37615         return this.el;
37616     },
37617
37618     /**
37619      * Adds a separator bar to the menu
37620      * @return {Roo.menu.Item} The menu item that was added
37621      */
37622     addSeparator : function(){
37623         return this.addItem(new Roo.menu.Separator());
37624     },
37625
37626     /**
37627      * Adds an {@link Roo.Element} object to the menu
37628      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37629      * @return {Roo.menu.Item} The menu item that was added
37630      */
37631     addElement : function(el){
37632         return this.addItem(new Roo.menu.BaseItem(el));
37633     },
37634
37635     /**
37636      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37637      * @param {Roo.menu.Item} item The menu item to add
37638      * @return {Roo.menu.Item} The menu item that was added
37639      */
37640     addItem : function(item){
37641         this.items.add(item);
37642         if(this.ul){
37643             var li = document.createElement("li");
37644             li.className = "x-menu-list-item";
37645             this.ul.dom.appendChild(li);
37646             item.render(li, this);
37647             this.delayAutoWidth();
37648         }
37649         return item;
37650     },
37651
37652     /**
37653      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37654      * @param {Object} config A MenuItem config object
37655      * @return {Roo.menu.Item} The menu item that was added
37656      */
37657     addMenuItem : function(config){
37658         if(!(config instanceof Roo.menu.Item)){
37659             if(typeof config.checked == "boolean"){ // must be check menu item config?
37660                 config = new Roo.menu.CheckItem(config);
37661             }else{
37662                 config = new Roo.menu.Item(config);
37663             }
37664         }
37665         return this.addItem(config);
37666     },
37667
37668     /**
37669      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37670      * @param {String} text The text to display in the menu item
37671      * @return {Roo.menu.Item} The menu item that was added
37672      */
37673     addText : function(text){
37674         return this.addItem(new Roo.menu.TextItem({ text : text }));
37675     },
37676
37677     /**
37678      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37679      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37680      * @param {Roo.menu.Item} item The menu item to add
37681      * @return {Roo.menu.Item} The menu item that was added
37682      */
37683     insert : function(index, item){
37684         this.items.insert(index, item);
37685         if(this.ul){
37686             var li = document.createElement("li");
37687             li.className = "x-menu-list-item";
37688             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37689             item.render(li, this);
37690             this.delayAutoWidth();
37691         }
37692         return item;
37693     },
37694
37695     /**
37696      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37697      * @param {Roo.menu.Item} item The menu item to remove
37698      */
37699     remove : function(item){
37700         this.items.removeKey(item.id);
37701         item.destroy();
37702     },
37703
37704     /**
37705      * Removes and destroys all items in the menu
37706      */
37707     removeAll : function(){
37708         var f;
37709         while(f = this.items.first()){
37710             this.remove(f);
37711         }
37712     }
37713 });
37714
37715 // MenuNav is a private utility class used internally by the Menu
37716 Roo.menu.MenuNav = function(menu){
37717     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37718     this.scope = this.menu = menu;
37719 };
37720
37721 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37722     doRelay : function(e, h){
37723         var k = e.getKey();
37724         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37725             this.menu.tryActivate(0, 1);
37726             return false;
37727         }
37728         return h.call(this.scope || this, e, this.menu);
37729     },
37730
37731     up : function(e, m){
37732         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37733             m.tryActivate(m.items.length-1, -1);
37734         }
37735     },
37736
37737     down : function(e, m){
37738         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37739             m.tryActivate(0, 1);
37740         }
37741     },
37742
37743     right : function(e, m){
37744         if(m.activeItem){
37745             m.activeItem.expandMenu(true);
37746         }
37747     },
37748
37749     left : function(e, m){
37750         m.hide();
37751         if(m.parentMenu && m.parentMenu.activeItem){
37752             m.parentMenu.activeItem.activate();
37753         }
37754     },
37755
37756     enter : function(e, m){
37757         if(m.activeItem){
37758             e.stopPropagation();
37759             m.activeItem.onClick(e);
37760             m.fireEvent("click", this, m.activeItem);
37761             return true;
37762         }
37763     }
37764 });/*
37765  * Based on:
37766  * Ext JS Library 1.1.1
37767  * Copyright(c) 2006-2007, Ext JS, LLC.
37768  *
37769  * Originally Released Under LGPL - original licence link has changed is not relivant.
37770  *
37771  * Fork - LGPL
37772  * <script type="text/javascript">
37773  */
37774  
37775 /**
37776  * @class Roo.menu.MenuMgr
37777  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37778  * @singleton
37779  */
37780 Roo.menu.MenuMgr = function(){
37781    var menus, active, groups = {}, attached = false, lastShow = new Date();
37782
37783    // private - called when first menu is created
37784    function init(){
37785        menus = {};
37786        active = new Roo.util.MixedCollection();
37787        Roo.get(document).addKeyListener(27, function(){
37788            if(active.length > 0){
37789                hideAll();
37790            }
37791        });
37792    }
37793
37794    // private
37795    function hideAll(){
37796        if(active && active.length > 0){
37797            var c = active.clone();
37798            c.each(function(m){
37799                m.hide();
37800            });
37801        }
37802    }
37803
37804    // private
37805    function onHide(m){
37806        active.remove(m);
37807        if(active.length < 1){
37808            Roo.get(document).un("mousedown", onMouseDown);
37809            attached = false;
37810        }
37811    }
37812
37813    // private
37814    function onShow(m){
37815        var last = active.last();
37816        lastShow = new Date();
37817        active.add(m);
37818        if(!attached){
37819            Roo.get(document).on("mousedown", onMouseDown);
37820            attached = true;
37821        }
37822        if(m.parentMenu){
37823           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37824           m.parentMenu.activeChild = m;
37825        }else if(last && last.isVisible()){
37826           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37827        }
37828    }
37829
37830    // private
37831    function onBeforeHide(m){
37832        if(m.activeChild){
37833            m.activeChild.hide();
37834        }
37835        if(m.autoHideTimer){
37836            clearTimeout(m.autoHideTimer);
37837            delete m.autoHideTimer;
37838        }
37839    }
37840
37841    // private
37842    function onBeforeShow(m){
37843        var pm = m.parentMenu;
37844        if(!pm && !m.allowOtherMenus){
37845            hideAll();
37846        }else if(pm && pm.activeChild && active != m){
37847            pm.activeChild.hide();
37848        }
37849    }
37850
37851    // private
37852    function onMouseDown(e){
37853        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37854            hideAll();
37855        }
37856    }
37857
37858    // private
37859    function onBeforeCheck(mi, state){
37860        if(state){
37861            var g = groups[mi.group];
37862            for(var i = 0, l = g.length; i < l; i++){
37863                if(g[i] != mi){
37864                    g[i].setChecked(false);
37865                }
37866            }
37867        }
37868    }
37869
37870    return {
37871
37872        /**
37873         * Hides all menus that are currently visible
37874         */
37875        hideAll : function(){
37876             hideAll();  
37877        },
37878
37879        // private
37880        register : function(menu){
37881            if(!menus){
37882                init();
37883            }
37884            menus[menu.id] = menu;
37885            menu.on("beforehide", onBeforeHide);
37886            menu.on("hide", onHide);
37887            menu.on("beforeshow", onBeforeShow);
37888            menu.on("show", onShow);
37889            var g = menu.group;
37890            if(g && menu.events["checkchange"]){
37891                if(!groups[g]){
37892                    groups[g] = [];
37893                }
37894                groups[g].push(menu);
37895                menu.on("checkchange", onCheck);
37896            }
37897        },
37898
37899         /**
37900          * Returns a {@link Roo.menu.Menu} object
37901          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37902          * be used to generate and return a new Menu instance.
37903          */
37904        get : function(menu){
37905            if(typeof menu == "string"){ // menu id
37906                return menus[menu];
37907            }else if(menu.events){  // menu instance
37908                return menu;
37909            }else if(typeof menu.length == 'number'){ // array of menu items?
37910                return new Roo.menu.Menu({items:menu});
37911            }else{ // otherwise, must be a config
37912                return new Roo.menu.Menu(menu);
37913            }
37914        },
37915
37916        // private
37917        unregister : function(menu){
37918            delete menus[menu.id];
37919            menu.un("beforehide", onBeforeHide);
37920            menu.un("hide", onHide);
37921            menu.un("beforeshow", onBeforeShow);
37922            menu.un("show", onShow);
37923            var g = menu.group;
37924            if(g && menu.events["checkchange"]){
37925                groups[g].remove(menu);
37926                menu.un("checkchange", onCheck);
37927            }
37928        },
37929
37930        // private
37931        registerCheckable : function(menuItem){
37932            var g = menuItem.group;
37933            if(g){
37934                if(!groups[g]){
37935                    groups[g] = [];
37936                }
37937                groups[g].push(menuItem);
37938                menuItem.on("beforecheckchange", onBeforeCheck);
37939            }
37940        },
37941
37942        // private
37943        unregisterCheckable : function(menuItem){
37944            var g = menuItem.group;
37945            if(g){
37946                groups[g].remove(menuItem);
37947                menuItem.un("beforecheckchange", onBeforeCheck);
37948            }
37949        }
37950    };
37951 }();/*
37952  * Based on:
37953  * Ext JS Library 1.1.1
37954  * Copyright(c) 2006-2007, Ext JS, LLC.
37955  *
37956  * Originally Released Under LGPL - original licence link has changed is not relivant.
37957  *
37958  * Fork - LGPL
37959  * <script type="text/javascript">
37960  */
37961  
37962
37963 /**
37964  * @class Roo.menu.BaseItem
37965  * @extends Roo.Component
37966  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37967  * management and base configuration options shared by all menu components.
37968  * @constructor
37969  * Creates a new BaseItem
37970  * @param {Object} config Configuration options
37971  */
37972 Roo.menu.BaseItem = function(config){
37973     Roo.menu.BaseItem.superclass.constructor.call(this, config);
37974
37975     this.addEvents({
37976         /**
37977          * @event click
37978          * Fires when this item is clicked
37979          * @param {Roo.menu.BaseItem} this
37980          * @param {Roo.EventObject} e
37981          */
37982         click: true,
37983         /**
37984          * @event activate
37985          * Fires when this item is activated
37986          * @param {Roo.menu.BaseItem} this
37987          */
37988         activate : true,
37989         /**
37990          * @event deactivate
37991          * Fires when this item is deactivated
37992          * @param {Roo.menu.BaseItem} this
37993          */
37994         deactivate : true
37995     });
37996
37997     if(this.handler){
37998         this.on("click", this.handler, this.scope, true);
37999     }
38000 };
38001
38002 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38003     /**
38004      * @cfg {Function} handler
38005      * A function that will handle the click event of this menu item (defaults to undefined)
38006      */
38007     /**
38008      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38009      */
38010     canActivate : false,
38011     
38012      /**
38013      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38014      */
38015     hidden: false,
38016     
38017     /**
38018      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38019      */
38020     activeClass : "x-menu-item-active",
38021     /**
38022      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38023      */
38024     hideOnClick : true,
38025     /**
38026      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38027      */
38028     hideDelay : 100,
38029
38030     // private
38031     ctype: "Roo.menu.BaseItem",
38032
38033     // private
38034     actionMode : "container",
38035
38036     // private
38037     render : function(container, parentMenu){
38038         this.parentMenu = parentMenu;
38039         Roo.menu.BaseItem.superclass.render.call(this, container);
38040         this.container.menuItemId = this.id;
38041     },
38042
38043     // private
38044     onRender : function(container, position){
38045         this.el = Roo.get(this.el);
38046         container.dom.appendChild(this.el.dom);
38047     },
38048
38049     // private
38050     onClick : function(e){
38051         if(!this.disabled && this.fireEvent("click", this, e) !== false
38052                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38053             this.handleClick(e);
38054         }else{
38055             e.stopEvent();
38056         }
38057     },
38058
38059     // private
38060     activate : function(){
38061         if(this.disabled){
38062             return false;
38063         }
38064         var li = this.container;
38065         li.addClass(this.activeClass);
38066         this.region = li.getRegion().adjust(2, 2, -2, -2);
38067         this.fireEvent("activate", this);
38068         return true;
38069     },
38070
38071     // private
38072     deactivate : function(){
38073         this.container.removeClass(this.activeClass);
38074         this.fireEvent("deactivate", this);
38075     },
38076
38077     // private
38078     shouldDeactivate : function(e){
38079         return !this.region || !this.region.contains(e.getPoint());
38080     },
38081
38082     // private
38083     handleClick : function(e){
38084         if(this.hideOnClick){
38085             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38086         }
38087     },
38088
38089     // private
38090     expandMenu : function(autoActivate){
38091         // do nothing
38092     },
38093
38094     // private
38095     hideMenu : function(){
38096         // do nothing
38097     }
38098 });/*
38099  * Based on:
38100  * Ext JS Library 1.1.1
38101  * Copyright(c) 2006-2007, Ext JS, LLC.
38102  *
38103  * Originally Released Under LGPL - original licence link has changed is not relivant.
38104  *
38105  * Fork - LGPL
38106  * <script type="text/javascript">
38107  */
38108  
38109 /**
38110  * @class Roo.menu.Adapter
38111  * @extends Roo.menu.BaseItem
38112  * 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.
38113  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38114  * @constructor
38115  * Creates a new Adapter
38116  * @param {Object} config Configuration options
38117  */
38118 Roo.menu.Adapter = function(component, config){
38119     Roo.menu.Adapter.superclass.constructor.call(this, config);
38120     this.component = component;
38121 };
38122 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38123     // private
38124     canActivate : true,
38125
38126     // private
38127     onRender : function(container, position){
38128         this.component.render(container);
38129         this.el = this.component.getEl();
38130     },
38131
38132     // private
38133     activate : function(){
38134         if(this.disabled){
38135             return false;
38136         }
38137         this.component.focus();
38138         this.fireEvent("activate", this);
38139         return true;
38140     },
38141
38142     // private
38143     deactivate : function(){
38144         this.fireEvent("deactivate", this);
38145     },
38146
38147     // private
38148     disable : function(){
38149         this.component.disable();
38150         Roo.menu.Adapter.superclass.disable.call(this);
38151     },
38152
38153     // private
38154     enable : function(){
38155         this.component.enable();
38156         Roo.menu.Adapter.superclass.enable.call(this);
38157     }
38158 });/*
38159  * Based on:
38160  * Ext JS Library 1.1.1
38161  * Copyright(c) 2006-2007, Ext JS, LLC.
38162  *
38163  * Originally Released Under LGPL - original licence link has changed is not relivant.
38164  *
38165  * Fork - LGPL
38166  * <script type="text/javascript">
38167  */
38168
38169 /**
38170  * @class Roo.menu.TextItem
38171  * @extends Roo.menu.BaseItem
38172  * Adds a static text string to a menu, usually used as either a heading or group separator.
38173  * Note: old style constructor with text is still supported.
38174  * 
38175  * @constructor
38176  * Creates a new TextItem
38177  * @param {Object} cfg Configuration
38178  */
38179 Roo.menu.TextItem = function(cfg){
38180     if (typeof(cfg) == 'string') {
38181         this.text = cfg;
38182     } else {
38183         Roo.apply(this,cfg);
38184     }
38185     
38186     Roo.menu.TextItem.superclass.constructor.call(this);
38187 };
38188
38189 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38190     /**
38191      * @cfg {Boolean} text Text to show on item.
38192      */
38193     text : '',
38194     
38195     /**
38196      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38197      */
38198     hideOnClick : false,
38199     /**
38200      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38201      */
38202     itemCls : "x-menu-text",
38203
38204     // private
38205     onRender : function(){
38206         var s = document.createElement("span");
38207         s.className = this.itemCls;
38208         s.innerHTML = this.text;
38209         this.el = s;
38210         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38211     }
38212 });/*
38213  * Based on:
38214  * Ext JS Library 1.1.1
38215  * Copyright(c) 2006-2007, Ext JS, LLC.
38216  *
38217  * Originally Released Under LGPL - original licence link has changed is not relivant.
38218  *
38219  * Fork - LGPL
38220  * <script type="text/javascript">
38221  */
38222
38223 /**
38224  * @class Roo.menu.Separator
38225  * @extends Roo.menu.BaseItem
38226  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38227  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38228  * @constructor
38229  * @param {Object} config Configuration options
38230  */
38231 Roo.menu.Separator = function(config){
38232     Roo.menu.Separator.superclass.constructor.call(this, config);
38233 };
38234
38235 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38236     /**
38237      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38238      */
38239     itemCls : "x-menu-sep",
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     // private
38246     onRender : function(li){
38247         var s = document.createElement("span");
38248         s.className = this.itemCls;
38249         s.innerHTML = "&#160;";
38250         this.el = s;
38251         li.addClass("x-menu-sep-li");
38252         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38253     }
38254 });/*
38255  * Based on:
38256  * Ext JS Library 1.1.1
38257  * Copyright(c) 2006-2007, Ext JS, LLC.
38258  *
38259  * Originally Released Under LGPL - original licence link has changed is not relivant.
38260  *
38261  * Fork - LGPL
38262  * <script type="text/javascript">
38263  */
38264 /**
38265  * @class Roo.menu.Item
38266  * @extends Roo.menu.BaseItem
38267  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38268  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38269  * activation and click handling.
38270  * @constructor
38271  * Creates a new Item
38272  * @param {Object} config Configuration options
38273  */
38274 Roo.menu.Item = function(config){
38275     Roo.menu.Item.superclass.constructor.call(this, config);
38276     if(this.menu){
38277         this.menu = Roo.menu.MenuMgr.get(this.menu);
38278     }
38279 };
38280 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38281     
38282     /**
38283      * @cfg {String} text
38284      * The text to show on the menu item.
38285      */
38286     text: '',
38287      /**
38288      * @cfg {String} HTML to render in menu
38289      * The text to show on the menu item (HTML version).
38290      */
38291     html: '',
38292     /**
38293      * @cfg {String} icon
38294      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38295      */
38296     icon: undefined,
38297     /**
38298      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38299      */
38300     itemCls : "x-menu-item",
38301     /**
38302      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38303      */
38304     canActivate : true,
38305     /**
38306      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38307      */
38308     showDelay: 200,
38309     // doc'd in BaseItem
38310     hideDelay: 200,
38311
38312     // private
38313     ctype: "Roo.menu.Item",
38314     
38315     // private
38316     onRender : function(container, position){
38317         var el = document.createElement("a");
38318         el.hideFocus = true;
38319         el.unselectable = "on";
38320         el.href = this.href || "#";
38321         if(this.hrefTarget){
38322             el.target = this.hrefTarget;
38323         }
38324         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38325         
38326         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38327         
38328         el.innerHTML = String.format(
38329                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38330                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38331         this.el = el;
38332         Roo.menu.Item.superclass.onRender.call(this, container, position);
38333     },
38334
38335     /**
38336      * Sets the text to display in this menu item
38337      * @param {String} text The text to display
38338      * @param {Boolean} isHTML true to indicate text is pure html.
38339      */
38340     setText : function(text, isHTML){
38341         if (isHTML) {
38342             this.html = text;
38343         } else {
38344             this.text = text;
38345             this.html = '';
38346         }
38347         if(this.rendered){
38348             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38349      
38350             this.el.update(String.format(
38351                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38352                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38353             this.parentMenu.autoWidth();
38354         }
38355     },
38356
38357     // private
38358     handleClick : function(e){
38359         if(!this.href){ // if no link defined, stop the event automatically
38360             e.stopEvent();
38361         }
38362         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38363     },
38364
38365     // private
38366     activate : function(autoExpand){
38367         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38368             this.focus();
38369             if(autoExpand){
38370                 this.expandMenu();
38371             }
38372         }
38373         return true;
38374     },
38375
38376     // private
38377     shouldDeactivate : function(e){
38378         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38379             if(this.menu && this.menu.isVisible()){
38380                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38381             }
38382             return true;
38383         }
38384         return false;
38385     },
38386
38387     // private
38388     deactivate : function(){
38389         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38390         this.hideMenu();
38391     },
38392
38393     // private
38394     expandMenu : function(autoActivate){
38395         if(!this.disabled && this.menu){
38396             clearTimeout(this.hideTimer);
38397             delete this.hideTimer;
38398             if(!this.menu.isVisible() && !this.showTimer){
38399                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38400             }else if (this.menu.isVisible() && autoActivate){
38401                 this.menu.tryActivate(0, 1);
38402             }
38403         }
38404     },
38405
38406     // private
38407     deferExpand : function(autoActivate){
38408         delete this.showTimer;
38409         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38410         if(autoActivate){
38411             this.menu.tryActivate(0, 1);
38412         }
38413     },
38414
38415     // private
38416     hideMenu : function(){
38417         clearTimeout(this.showTimer);
38418         delete this.showTimer;
38419         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38420             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38421         }
38422     },
38423
38424     // private
38425     deferHide : function(){
38426         delete this.hideTimer;
38427         this.menu.hide();
38428     }
38429 });/*
38430  * Based on:
38431  * Ext JS Library 1.1.1
38432  * Copyright(c) 2006-2007, Ext JS, LLC.
38433  *
38434  * Originally Released Under LGPL - original licence link has changed is not relivant.
38435  *
38436  * Fork - LGPL
38437  * <script type="text/javascript">
38438  */
38439  
38440 /**
38441  * @class Roo.menu.CheckItem
38442  * @extends Roo.menu.Item
38443  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38444  * @constructor
38445  * Creates a new CheckItem
38446  * @param {Object} config Configuration options
38447  */
38448 Roo.menu.CheckItem = function(config){
38449     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38450     this.addEvents({
38451         /**
38452          * @event beforecheckchange
38453          * Fires before the checked value is set, providing an opportunity to cancel if needed
38454          * @param {Roo.menu.CheckItem} this
38455          * @param {Boolean} checked The new checked value that will be set
38456          */
38457         "beforecheckchange" : true,
38458         /**
38459          * @event checkchange
38460          * Fires after the checked value has been set
38461          * @param {Roo.menu.CheckItem} this
38462          * @param {Boolean} checked The checked value that was set
38463          */
38464         "checkchange" : true
38465     });
38466     if(this.checkHandler){
38467         this.on('checkchange', this.checkHandler, this.scope);
38468     }
38469 };
38470 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38471     /**
38472      * @cfg {String} group
38473      * All check items with the same group name will automatically be grouped into a single-select
38474      * radio button group (defaults to '')
38475      */
38476     /**
38477      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38478      */
38479     itemCls : "x-menu-item x-menu-check-item",
38480     /**
38481      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38482      */
38483     groupClass : "x-menu-group-item",
38484
38485     /**
38486      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38487      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38488      * initialized with checked = true will be rendered as checked.
38489      */
38490     checked: false,
38491
38492     // private
38493     ctype: "Roo.menu.CheckItem",
38494
38495     // private
38496     onRender : function(c){
38497         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38498         if(this.group){
38499             this.el.addClass(this.groupClass);
38500         }
38501         Roo.menu.MenuMgr.registerCheckable(this);
38502         if(this.checked){
38503             this.checked = false;
38504             this.setChecked(true, true);
38505         }
38506     },
38507
38508     // private
38509     destroy : function(){
38510         if(this.rendered){
38511             Roo.menu.MenuMgr.unregisterCheckable(this);
38512         }
38513         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38514     },
38515
38516     /**
38517      * Set the checked state of this item
38518      * @param {Boolean} checked The new checked value
38519      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38520      */
38521     setChecked : function(state, suppressEvent){
38522         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38523             if(this.container){
38524                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38525             }
38526             this.checked = state;
38527             if(suppressEvent !== true){
38528                 this.fireEvent("checkchange", this, state);
38529             }
38530         }
38531     },
38532
38533     // private
38534     handleClick : function(e){
38535        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38536            this.setChecked(!this.checked);
38537        }
38538        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38539     }
38540 });/*
38541  * Based on:
38542  * Ext JS Library 1.1.1
38543  * Copyright(c) 2006-2007, Ext JS, LLC.
38544  *
38545  * Originally Released Under LGPL - original licence link has changed is not relivant.
38546  *
38547  * Fork - LGPL
38548  * <script type="text/javascript">
38549  */
38550  
38551 /**
38552  * @class Roo.menu.DateItem
38553  * @extends Roo.menu.Adapter
38554  * A menu item that wraps the {@link Roo.DatPicker} component.
38555  * @constructor
38556  * Creates a new DateItem
38557  * @param {Object} config Configuration options
38558  */
38559 Roo.menu.DateItem = function(config){
38560     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38561     /** The Roo.DatePicker object @type Roo.DatePicker */
38562     this.picker = this.component;
38563     this.addEvents({select: true});
38564     
38565     this.picker.on("render", function(picker){
38566         picker.getEl().swallowEvent("click");
38567         picker.container.addClass("x-menu-date-item");
38568     });
38569
38570     this.picker.on("select", this.onSelect, this);
38571 };
38572
38573 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38574     // private
38575     onSelect : function(picker, date){
38576         this.fireEvent("select", this, date, picker);
38577         Roo.menu.DateItem.superclass.handleClick.call(this);
38578     }
38579 });/*
38580  * Based on:
38581  * Ext JS Library 1.1.1
38582  * Copyright(c) 2006-2007, Ext JS, LLC.
38583  *
38584  * Originally Released Under LGPL - original licence link has changed is not relivant.
38585  *
38586  * Fork - LGPL
38587  * <script type="text/javascript">
38588  */
38589  
38590 /**
38591  * @class Roo.menu.ColorItem
38592  * @extends Roo.menu.Adapter
38593  * A menu item that wraps the {@link Roo.ColorPalette} component.
38594  * @constructor
38595  * Creates a new ColorItem
38596  * @param {Object} config Configuration options
38597  */
38598 Roo.menu.ColorItem = function(config){
38599     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38600     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38601     this.palette = this.component;
38602     this.relayEvents(this.palette, ["select"]);
38603     if(this.selectHandler){
38604         this.on('select', this.selectHandler, this.scope);
38605     }
38606 };
38607 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38608  * Based on:
38609  * Ext JS Library 1.1.1
38610  * Copyright(c) 2006-2007, Ext JS, LLC.
38611  *
38612  * Originally Released Under LGPL - original licence link has changed is not relivant.
38613  *
38614  * Fork - LGPL
38615  * <script type="text/javascript">
38616  */
38617  
38618
38619 /**
38620  * @class Roo.menu.DateMenu
38621  * @extends Roo.menu.Menu
38622  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38623  * @constructor
38624  * Creates a new DateMenu
38625  * @param {Object} config Configuration options
38626  */
38627 Roo.menu.DateMenu = function(config){
38628     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38629     this.plain = true;
38630     var di = new Roo.menu.DateItem(config);
38631     this.add(di);
38632     /**
38633      * The {@link Roo.DatePicker} instance for this DateMenu
38634      * @type DatePicker
38635      */
38636     this.picker = di.picker;
38637     /**
38638      * @event select
38639      * @param {DatePicker} picker
38640      * @param {Date} date
38641      */
38642     this.relayEvents(di, ["select"]);
38643     this.on('beforeshow', function(){
38644         if(this.picker){
38645             this.picker.hideMonthPicker(false);
38646         }
38647     }, this);
38648 };
38649 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38650     cls:'x-date-menu'
38651 });/*
38652  * Based on:
38653  * Ext JS Library 1.1.1
38654  * Copyright(c) 2006-2007, Ext JS, LLC.
38655  *
38656  * Originally Released Under LGPL - original licence link has changed is not relivant.
38657  *
38658  * Fork - LGPL
38659  * <script type="text/javascript">
38660  */
38661  
38662
38663 /**
38664  * @class Roo.menu.ColorMenu
38665  * @extends Roo.menu.Menu
38666  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38667  * @constructor
38668  * Creates a new ColorMenu
38669  * @param {Object} config Configuration options
38670  */
38671 Roo.menu.ColorMenu = function(config){
38672     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38673     this.plain = true;
38674     var ci = new Roo.menu.ColorItem(config);
38675     this.add(ci);
38676     /**
38677      * The {@link Roo.ColorPalette} instance for this ColorMenu
38678      * @type ColorPalette
38679      */
38680     this.palette = ci.palette;
38681     /**
38682      * @event select
38683      * @param {ColorPalette} palette
38684      * @param {String} color
38685      */
38686     this.relayEvents(ci, ["select"]);
38687 };
38688 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38689  * Based on:
38690  * Ext JS Library 1.1.1
38691  * Copyright(c) 2006-2007, Ext JS, LLC.
38692  *
38693  * Originally Released Under LGPL - original licence link has changed is not relivant.
38694  *
38695  * Fork - LGPL
38696  * <script type="text/javascript">
38697  */
38698  
38699 /**
38700  * @class Roo.form.Field
38701  * @extends Roo.BoxComponent
38702  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38703  * @constructor
38704  * Creates a new Field
38705  * @param {Object} config Configuration options
38706  */
38707 Roo.form.Field = function(config){
38708     Roo.form.Field.superclass.constructor.call(this, config);
38709 };
38710
38711 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38712     /**
38713      * @cfg {String} fieldLabel Label to use when rendering a form.
38714      */
38715        /**
38716      * @cfg {String} qtip Mouse over tip
38717      */
38718      
38719     /**
38720      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38721      */
38722     invalidClass : "x-form-invalid",
38723     /**
38724      * @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")
38725      */
38726     invalidText : "The value in this field is invalid",
38727     /**
38728      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38729      */
38730     focusClass : "x-form-focus",
38731     /**
38732      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38733       automatic validation (defaults to "keyup").
38734      */
38735     validationEvent : "keyup",
38736     /**
38737      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38738      */
38739     validateOnBlur : true,
38740     /**
38741      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38742      */
38743     validationDelay : 250,
38744     /**
38745      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38746      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38747      */
38748     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38749     /**
38750      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38751      */
38752     fieldClass : "x-form-field",
38753     /**
38754      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38755      *<pre>
38756 Value         Description
38757 -----------   ----------------------------------------------------------------------
38758 qtip          Display a quick tip when the user hovers over the field
38759 title         Display a default browser title attribute popup
38760 under         Add a block div beneath the field containing the error text
38761 side          Add an error icon to the right of the field with a popup on hover
38762 [element id]  Add the error text directly to the innerHTML of the specified element
38763 </pre>
38764      */
38765     msgTarget : 'qtip',
38766     /**
38767      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38768      */
38769     msgFx : 'normal',
38770
38771     /**
38772      * @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.
38773      */
38774     readOnly : false,
38775
38776     /**
38777      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38778      */
38779     disabled : false,
38780
38781     /**
38782      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38783      */
38784     inputType : undefined,
38785     
38786     /**
38787      * @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).
38788          */
38789         tabIndex : undefined,
38790         
38791     // private
38792     isFormField : true,
38793
38794     // private
38795     hasFocus : false,
38796     /**
38797      * @property {Roo.Element} fieldEl
38798      * Element Containing the rendered Field (with label etc.)
38799      */
38800     /**
38801      * @cfg {Mixed} value A value to initialize this field with.
38802      */
38803     value : undefined,
38804
38805     /**
38806      * @cfg {String} name The field's HTML name attribute.
38807      */
38808     /**
38809      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38810      */
38811     // private
38812     loadedValue : false,
38813      
38814      
38815         // private ??
38816         initComponent : function(){
38817         Roo.form.Field.superclass.initComponent.call(this);
38818         this.addEvents({
38819             /**
38820              * @event focus
38821              * Fires when this field receives input focus.
38822              * @param {Roo.form.Field} this
38823              */
38824             focus : true,
38825             /**
38826              * @event blur
38827              * Fires when this field loses input focus.
38828              * @param {Roo.form.Field} this
38829              */
38830             blur : true,
38831             /**
38832              * @event specialkey
38833              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38834              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38835              * @param {Roo.form.Field} this
38836              * @param {Roo.EventObject} e The event object
38837              */
38838             specialkey : true,
38839             /**
38840              * @event change
38841              * Fires just before the field blurs if the field value has changed.
38842              * @param {Roo.form.Field} this
38843              * @param {Mixed} newValue The new value
38844              * @param {Mixed} oldValue The original value
38845              */
38846             change : true,
38847             /**
38848              * @event invalid
38849              * Fires after the field has been marked as invalid.
38850              * @param {Roo.form.Field} this
38851              * @param {String} msg The validation message
38852              */
38853             invalid : true,
38854             /**
38855              * @event valid
38856              * Fires after the field has been validated with no errors.
38857              * @param {Roo.form.Field} this
38858              */
38859             valid : true,
38860              /**
38861              * @event keyup
38862              * Fires after the key up
38863              * @param {Roo.form.Field} this
38864              * @param {Roo.EventObject}  e The event Object
38865              */
38866             keyup : true
38867         });
38868     },
38869
38870     /**
38871      * Returns the name attribute of the field if available
38872      * @return {String} name The field name
38873      */
38874     getName: function(){
38875          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38876     },
38877
38878     // private
38879     onRender : function(ct, position){
38880         Roo.form.Field.superclass.onRender.call(this, ct, position);
38881         if(!this.el){
38882             var cfg = this.getAutoCreate();
38883             if(!cfg.name){
38884                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38885             }
38886             if (!cfg.name.length) {
38887                 delete cfg.name;
38888             }
38889             if(this.inputType){
38890                 cfg.type = this.inputType;
38891             }
38892             this.el = ct.createChild(cfg, position);
38893         }
38894         var type = this.el.dom.type;
38895         if(type){
38896             if(type == 'password'){
38897                 type = 'text';
38898             }
38899             this.el.addClass('x-form-'+type);
38900         }
38901         if(this.readOnly){
38902             this.el.dom.readOnly = true;
38903         }
38904         if(this.tabIndex !== undefined){
38905             this.el.dom.setAttribute('tabIndex', this.tabIndex);
38906         }
38907
38908         this.el.addClass([this.fieldClass, this.cls]);
38909         this.initValue();
38910     },
38911
38912     /**
38913      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
38914      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
38915      * @return {Roo.form.Field} this
38916      */
38917     applyTo : function(target){
38918         this.allowDomMove = false;
38919         this.el = Roo.get(target);
38920         this.render(this.el.dom.parentNode);
38921         return this;
38922     },
38923
38924     // private
38925     initValue : function(){
38926         if(this.value !== undefined){
38927             this.setValue(this.value);
38928         }else if(this.el.dom.value.length > 0){
38929             this.setValue(this.el.dom.value);
38930         }
38931     },
38932
38933     /**
38934      * Returns true if this field has been changed since it was originally loaded and is not disabled.
38935      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
38936      */
38937     isDirty : function() {
38938         if(this.disabled) {
38939             return false;
38940         }
38941         return String(this.getValue()) !== String(this.originalValue);
38942     },
38943
38944     /**
38945      * stores the current value in loadedValue
38946      */
38947     resetHasChanged : function()
38948     {
38949         this.loadedValue = String(this.getValue());
38950     },
38951     /**
38952      * checks the current value against the 'loaded' value.
38953      * Note - will return false if 'resetHasChanged' has not been called first.
38954      */
38955     hasChanged : function()
38956     {
38957         if(this.disabled || this.readOnly) {
38958             return false;
38959         }
38960         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
38961     },
38962     
38963     
38964     
38965     // private
38966     afterRender : function(){
38967         Roo.form.Field.superclass.afterRender.call(this);
38968         this.initEvents();
38969     },
38970
38971     // private
38972     fireKey : function(e){
38973         //Roo.log('field ' + e.getKey());
38974         if(e.isNavKeyPress()){
38975             this.fireEvent("specialkey", this, e);
38976         }
38977     },
38978
38979     /**
38980      * Resets the current field value to the originally loaded value and clears any validation messages
38981      */
38982     reset : function(){
38983         this.setValue(this.resetValue);
38984         this.originalValue = this.getValue();
38985         this.clearInvalid();
38986     },
38987
38988     // private
38989     initEvents : function(){
38990         // safari killled keypress - so keydown is now used..
38991         this.el.on("keydown" , this.fireKey,  this);
38992         this.el.on("focus", this.onFocus,  this);
38993         this.el.on("blur", this.onBlur,  this);
38994         this.el.relayEvent('keyup', this);
38995
38996         // reference to original value for reset
38997         this.originalValue = this.getValue();
38998         this.resetValue =  this.getValue();
38999     },
39000
39001     // private
39002     onFocus : function(){
39003         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39004             this.el.addClass(this.focusClass);
39005         }
39006         if(!this.hasFocus){
39007             this.hasFocus = true;
39008             this.startValue = this.getValue();
39009             this.fireEvent("focus", this);
39010         }
39011     },
39012
39013     beforeBlur : Roo.emptyFn,
39014
39015     // private
39016     onBlur : function(){
39017         this.beforeBlur();
39018         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39019             this.el.removeClass(this.focusClass);
39020         }
39021         this.hasFocus = false;
39022         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39023             this.validate();
39024         }
39025         var v = this.getValue();
39026         if(String(v) !== String(this.startValue)){
39027             this.fireEvent('change', this, v, this.startValue);
39028         }
39029         this.fireEvent("blur", this);
39030     },
39031
39032     /**
39033      * Returns whether or not the field value is currently valid
39034      * @param {Boolean} preventMark True to disable marking the field invalid
39035      * @return {Boolean} True if the value is valid, else false
39036      */
39037     isValid : function(preventMark){
39038         if(this.disabled){
39039             return true;
39040         }
39041         var restore = this.preventMark;
39042         this.preventMark = preventMark === true;
39043         var v = this.validateValue(this.processValue(this.getRawValue()));
39044         this.preventMark = restore;
39045         return v;
39046     },
39047
39048     /**
39049      * Validates the field value
39050      * @return {Boolean} True if the value is valid, else false
39051      */
39052     validate : function(){
39053         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39054             this.clearInvalid();
39055             return true;
39056         }
39057         return false;
39058     },
39059
39060     processValue : function(value){
39061         return value;
39062     },
39063
39064     // private
39065     // Subclasses should provide the validation implementation by overriding this
39066     validateValue : function(value){
39067         return true;
39068     },
39069
39070     /**
39071      * Mark this field as invalid
39072      * @param {String} msg The validation message
39073      */
39074     markInvalid : function(msg){
39075         if(!this.rendered || this.preventMark){ // not rendered
39076             return;
39077         }
39078         
39079         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39080         
39081         obj.el.addClass(this.invalidClass);
39082         msg = msg || this.invalidText;
39083         switch(this.msgTarget){
39084             case 'qtip':
39085                 obj.el.dom.qtip = msg;
39086                 obj.el.dom.qclass = 'x-form-invalid-tip';
39087                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39088                     Roo.QuickTips.enable();
39089                 }
39090                 break;
39091             case 'title':
39092                 this.el.dom.title = msg;
39093                 break;
39094             case 'under':
39095                 if(!this.errorEl){
39096                     var elp = this.el.findParent('.x-form-element', 5, true);
39097                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39098                     this.errorEl.setWidth(elp.getWidth(true)-20);
39099                 }
39100                 this.errorEl.update(msg);
39101                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39102                 break;
39103             case 'side':
39104                 if(!this.errorIcon){
39105                     var elp = this.el.findParent('.x-form-element', 5, true);
39106                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39107                 }
39108                 this.alignErrorIcon();
39109                 this.errorIcon.dom.qtip = msg;
39110                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39111                 this.errorIcon.show();
39112                 this.on('resize', this.alignErrorIcon, this);
39113                 break;
39114             default:
39115                 var t = Roo.getDom(this.msgTarget);
39116                 t.innerHTML = msg;
39117                 t.style.display = this.msgDisplay;
39118                 break;
39119         }
39120         this.fireEvent('invalid', this, msg);
39121     },
39122
39123     // private
39124     alignErrorIcon : function(){
39125         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39126     },
39127
39128     /**
39129      * Clear any invalid styles/messages for this field
39130      */
39131     clearInvalid : function(){
39132         if(!this.rendered || this.preventMark){ // not rendered
39133             return;
39134         }
39135         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39136         
39137         obj.el.removeClass(this.invalidClass);
39138         switch(this.msgTarget){
39139             case 'qtip':
39140                 obj.el.dom.qtip = '';
39141                 break;
39142             case 'title':
39143                 this.el.dom.title = '';
39144                 break;
39145             case 'under':
39146                 if(this.errorEl){
39147                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39148                 }
39149                 break;
39150             case 'side':
39151                 if(this.errorIcon){
39152                     this.errorIcon.dom.qtip = '';
39153                     this.errorIcon.hide();
39154                     this.un('resize', this.alignErrorIcon, this);
39155                 }
39156                 break;
39157             default:
39158                 var t = Roo.getDom(this.msgTarget);
39159                 t.innerHTML = '';
39160                 t.style.display = 'none';
39161                 break;
39162         }
39163         this.fireEvent('valid', this);
39164     },
39165
39166     /**
39167      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39168      * @return {Mixed} value The field value
39169      */
39170     getRawValue : function(){
39171         var v = this.el.getValue();
39172         
39173         return v;
39174     },
39175
39176     /**
39177      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39178      * @return {Mixed} value The field value
39179      */
39180     getValue : function(){
39181         var v = this.el.getValue();
39182          
39183         return v;
39184     },
39185
39186     /**
39187      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39188      * @param {Mixed} value The value to set
39189      */
39190     setRawValue : function(v){
39191         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39192     },
39193
39194     /**
39195      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39196      * @param {Mixed} value The value to set
39197      */
39198     setValue : function(v){
39199         this.value = v;
39200         if(this.rendered){
39201             this.el.dom.value = (v === null || v === undefined ? '' : v);
39202              this.validate();
39203         }
39204     },
39205
39206     adjustSize : function(w, h){
39207         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39208         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39209         return s;
39210     },
39211
39212     adjustWidth : function(tag, w){
39213         tag = tag.toLowerCase();
39214         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39215             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39216                 if(tag == 'input'){
39217                     return w + 2;
39218                 }
39219                 if(tag == 'textarea'){
39220                     return w-2;
39221                 }
39222             }else if(Roo.isOpera){
39223                 if(tag == 'input'){
39224                     return w + 2;
39225                 }
39226                 if(tag == 'textarea'){
39227                     return w-2;
39228                 }
39229             }
39230         }
39231         return w;
39232     }
39233 });
39234
39235
39236 // anything other than normal should be considered experimental
39237 Roo.form.Field.msgFx = {
39238     normal : {
39239         show: function(msgEl, f){
39240             msgEl.setDisplayed('block');
39241         },
39242
39243         hide : function(msgEl, f){
39244             msgEl.setDisplayed(false).update('');
39245         }
39246     },
39247
39248     slide : {
39249         show: function(msgEl, f){
39250             msgEl.slideIn('t', {stopFx:true});
39251         },
39252
39253         hide : function(msgEl, f){
39254             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39255         }
39256     },
39257
39258     slideRight : {
39259         show: function(msgEl, f){
39260             msgEl.fixDisplay();
39261             msgEl.alignTo(f.el, 'tl-tr');
39262             msgEl.slideIn('l', {stopFx:true});
39263         },
39264
39265         hide : function(msgEl, f){
39266             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39267         }
39268     }
39269 };/*
39270  * Based on:
39271  * Ext JS Library 1.1.1
39272  * Copyright(c) 2006-2007, Ext JS, LLC.
39273  *
39274  * Originally Released Under LGPL - original licence link has changed is not relivant.
39275  *
39276  * Fork - LGPL
39277  * <script type="text/javascript">
39278  */
39279  
39280
39281 /**
39282  * @class Roo.form.TextField
39283  * @extends Roo.form.Field
39284  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39285  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39286  * @constructor
39287  * Creates a new TextField
39288  * @param {Object} config Configuration options
39289  */
39290 Roo.form.TextField = function(config){
39291     Roo.form.TextField.superclass.constructor.call(this, config);
39292     this.addEvents({
39293         /**
39294          * @event autosize
39295          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39296          * according to the default logic, but this event provides a hook for the developer to apply additional
39297          * logic at runtime to resize the field if needed.
39298              * @param {Roo.form.Field} this This text field
39299              * @param {Number} width The new field width
39300              */
39301         autosize : true
39302     });
39303 };
39304
39305 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39306     /**
39307      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39308      */
39309     grow : false,
39310     /**
39311      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39312      */
39313     growMin : 30,
39314     /**
39315      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39316      */
39317     growMax : 800,
39318     /**
39319      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39320      */
39321     vtype : null,
39322     /**
39323      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39324      */
39325     maskRe : null,
39326     /**
39327      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39328      */
39329     disableKeyFilter : false,
39330     /**
39331      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39332      */
39333     allowBlank : true,
39334     /**
39335      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39336      */
39337     minLength : 0,
39338     /**
39339      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39340      */
39341     maxLength : Number.MAX_VALUE,
39342     /**
39343      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39344      */
39345     minLengthText : "The minimum length for this field is {0}",
39346     /**
39347      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39348      */
39349     maxLengthText : "The maximum length for this field is {0}",
39350     /**
39351      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39352      */
39353     selectOnFocus : false,
39354     /**
39355      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39356      */
39357     blankText : "This field is required",
39358     /**
39359      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39360      * If available, this function will be called only after the basic validators all return true, and will be passed the
39361      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39362      */
39363     validator : null,
39364     /**
39365      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39366      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39367      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39368      */
39369     regex : null,
39370     /**
39371      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39372      */
39373     regexText : "",
39374     /**
39375      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39376      */
39377     emptyText : null,
39378    
39379
39380     // private
39381     initEvents : function()
39382     {
39383         if (this.emptyText) {
39384             this.el.attr('placeholder', this.emptyText);
39385         }
39386         
39387         Roo.form.TextField.superclass.initEvents.call(this);
39388         if(this.validationEvent == 'keyup'){
39389             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39390             this.el.on('keyup', this.filterValidation, this);
39391         }
39392         else if(this.validationEvent !== false){
39393             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39394         }
39395         
39396         if(this.selectOnFocus){
39397             this.on("focus", this.preFocus, this);
39398             
39399         }
39400         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39401             this.el.on("keypress", this.filterKeys, this);
39402         }
39403         if(this.grow){
39404             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39405             this.el.on("click", this.autoSize,  this);
39406         }
39407         if(this.el.is('input[type=password]') && Roo.isSafari){
39408             this.el.on('keydown', this.SafariOnKeyDown, this);
39409         }
39410     },
39411
39412     processValue : function(value){
39413         if(this.stripCharsRe){
39414             var newValue = value.replace(this.stripCharsRe, '');
39415             if(newValue !== value){
39416                 this.setRawValue(newValue);
39417                 return newValue;
39418             }
39419         }
39420         return value;
39421     },
39422
39423     filterValidation : function(e){
39424         if(!e.isNavKeyPress()){
39425             this.validationTask.delay(this.validationDelay);
39426         }
39427     },
39428
39429     // private
39430     onKeyUp : function(e){
39431         if(!e.isNavKeyPress()){
39432             this.autoSize();
39433         }
39434     },
39435
39436     /**
39437      * Resets the current field value to the originally-loaded value and clears any validation messages.
39438      *  
39439      */
39440     reset : function(){
39441         Roo.form.TextField.superclass.reset.call(this);
39442        
39443     },
39444
39445     
39446     // private
39447     preFocus : function(){
39448         
39449         if(this.selectOnFocus){
39450             this.el.dom.select();
39451         }
39452     },
39453
39454     
39455     // private
39456     filterKeys : function(e){
39457         var k = e.getKey();
39458         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39459             return;
39460         }
39461         var c = e.getCharCode(), cc = String.fromCharCode(c);
39462         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39463             return;
39464         }
39465         if(!this.maskRe.test(cc)){
39466             e.stopEvent();
39467         }
39468     },
39469
39470     setValue : function(v){
39471         
39472         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39473         
39474         this.autoSize();
39475     },
39476
39477     /**
39478      * Validates a value according to the field's validation rules and marks the field as invalid
39479      * if the validation fails
39480      * @param {Mixed} value The value to validate
39481      * @return {Boolean} True if the value is valid, else false
39482      */
39483     validateValue : function(value){
39484         if(value.length < 1)  { // if it's blank
39485              if(this.allowBlank){
39486                 this.clearInvalid();
39487                 return true;
39488              }else{
39489                 this.markInvalid(this.blankText);
39490                 return false;
39491              }
39492         }
39493         if(value.length < this.minLength){
39494             this.markInvalid(String.format(this.minLengthText, this.minLength));
39495             return false;
39496         }
39497         if(value.length > this.maxLength){
39498             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39499             return false;
39500         }
39501         if(this.vtype){
39502             var vt = Roo.form.VTypes;
39503             if(!vt[this.vtype](value, this)){
39504                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39505                 return false;
39506             }
39507         }
39508         if(typeof this.validator == "function"){
39509             var msg = this.validator(value);
39510             if(msg !== true){
39511                 this.markInvalid(msg);
39512                 return false;
39513             }
39514         }
39515         if(this.regex && !this.regex.test(value)){
39516             this.markInvalid(this.regexText);
39517             return false;
39518         }
39519         return true;
39520     },
39521
39522     /**
39523      * Selects text in this field
39524      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39525      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39526      */
39527     selectText : function(start, end){
39528         var v = this.getRawValue();
39529         if(v.length > 0){
39530             start = start === undefined ? 0 : start;
39531             end = end === undefined ? v.length : end;
39532             var d = this.el.dom;
39533             if(d.setSelectionRange){
39534                 d.setSelectionRange(start, end);
39535             }else if(d.createTextRange){
39536                 var range = d.createTextRange();
39537                 range.moveStart("character", start);
39538                 range.moveEnd("character", v.length-end);
39539                 range.select();
39540             }
39541         }
39542     },
39543
39544     /**
39545      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39546      * This only takes effect if grow = true, and fires the autosize event.
39547      */
39548     autoSize : function(){
39549         if(!this.grow || !this.rendered){
39550             return;
39551         }
39552         if(!this.metrics){
39553             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39554         }
39555         var el = this.el;
39556         var v = el.dom.value;
39557         var d = document.createElement('div');
39558         d.appendChild(document.createTextNode(v));
39559         v = d.innerHTML;
39560         d = null;
39561         v += "&#160;";
39562         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39563         this.el.setWidth(w);
39564         this.fireEvent("autosize", this, w);
39565     },
39566     
39567     // private
39568     SafariOnKeyDown : function(event)
39569     {
39570         // this is a workaround for a password hang bug on chrome/ webkit.
39571         
39572         var isSelectAll = false;
39573         
39574         if(this.el.dom.selectionEnd > 0){
39575             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39576         }
39577         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39578             event.preventDefault();
39579             this.setValue('');
39580             return;
39581         }
39582         
39583         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39584             
39585             event.preventDefault();
39586             // this is very hacky as keydown always get's upper case.
39587             
39588             var cc = String.fromCharCode(event.getCharCode());
39589             
39590             
39591             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39592             
39593         }
39594         
39595         
39596     }
39597 });/*
39598  * Based on:
39599  * Ext JS Library 1.1.1
39600  * Copyright(c) 2006-2007, Ext JS, LLC.
39601  *
39602  * Originally Released Under LGPL - original licence link has changed is not relivant.
39603  *
39604  * Fork - LGPL
39605  * <script type="text/javascript">
39606  */
39607  
39608 /**
39609  * @class Roo.form.Hidden
39610  * @extends Roo.form.TextField
39611  * Simple Hidden element used on forms 
39612  * 
39613  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39614  * 
39615  * @constructor
39616  * Creates a new Hidden form element.
39617  * @param {Object} config Configuration options
39618  */
39619
39620
39621
39622 // easy hidden field...
39623 Roo.form.Hidden = function(config){
39624     Roo.form.Hidden.superclass.constructor.call(this, config);
39625 };
39626   
39627 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39628     fieldLabel:      '',
39629     inputType:      'hidden',
39630     width:          50,
39631     allowBlank:     true,
39632     labelSeparator: '',
39633     hidden:         true,
39634     itemCls :       'x-form-item-display-none'
39635
39636
39637 });
39638
39639
39640 /*
39641  * Based on:
39642  * Ext JS Library 1.1.1
39643  * Copyright(c) 2006-2007, Ext JS, LLC.
39644  *
39645  * Originally Released Under LGPL - original licence link has changed is not relivant.
39646  *
39647  * Fork - LGPL
39648  * <script type="text/javascript">
39649  */
39650  
39651 /**
39652  * @class Roo.form.TriggerField
39653  * @extends Roo.form.TextField
39654  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39655  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39656  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39657  * for which you can provide a custom implementation.  For example:
39658  * <pre><code>
39659 var trigger = new Roo.form.TriggerField();
39660 trigger.onTriggerClick = myTriggerFn;
39661 trigger.applyTo('my-field');
39662 </code></pre>
39663  *
39664  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39665  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39666  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39667  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39668  * @constructor
39669  * Create a new TriggerField.
39670  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39671  * to the base TextField)
39672  */
39673 Roo.form.TriggerField = function(config){
39674     this.mimicing = false;
39675     Roo.form.TriggerField.superclass.constructor.call(this, config);
39676 };
39677
39678 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39679     /**
39680      * @cfg {String} triggerClass A CSS class to apply to the trigger
39681      */
39682     /**
39683      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39684      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39685      */
39686     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39687     /**
39688      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39689      */
39690     hideTrigger:false,
39691
39692     /** @cfg {Boolean} grow @hide */
39693     /** @cfg {Number} growMin @hide */
39694     /** @cfg {Number} growMax @hide */
39695
39696     /**
39697      * @hide 
39698      * @method
39699      */
39700     autoSize: Roo.emptyFn,
39701     // private
39702     monitorTab : true,
39703     // private
39704     deferHeight : true,
39705
39706     
39707     actionMode : 'wrap',
39708     // private
39709     onResize : function(w, h){
39710         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39711         if(typeof w == 'number'){
39712             var x = w - this.trigger.getWidth();
39713             this.el.setWidth(this.adjustWidth('input', x));
39714             this.trigger.setStyle('left', x+'px');
39715         }
39716     },
39717
39718     // private
39719     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39720
39721     // private
39722     getResizeEl : function(){
39723         return this.wrap;
39724     },
39725
39726     // private
39727     getPositionEl : function(){
39728         return this.wrap;
39729     },
39730
39731     // private
39732     alignErrorIcon : function(){
39733         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39734     },
39735
39736     // private
39737     onRender : function(ct, position){
39738         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39739         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39740         this.trigger = this.wrap.createChild(this.triggerConfig ||
39741                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39742         if(this.hideTrigger){
39743             this.trigger.setDisplayed(false);
39744         }
39745         this.initTrigger();
39746         if(!this.width){
39747             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39748         }
39749     },
39750
39751     // private
39752     initTrigger : function(){
39753         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39754         this.trigger.addClassOnOver('x-form-trigger-over');
39755         this.trigger.addClassOnClick('x-form-trigger-click');
39756     },
39757
39758     // private
39759     onDestroy : function(){
39760         if(this.trigger){
39761             this.trigger.removeAllListeners();
39762             this.trigger.remove();
39763         }
39764         if(this.wrap){
39765             this.wrap.remove();
39766         }
39767         Roo.form.TriggerField.superclass.onDestroy.call(this);
39768     },
39769
39770     // private
39771     onFocus : function(){
39772         Roo.form.TriggerField.superclass.onFocus.call(this);
39773         if(!this.mimicing){
39774             this.wrap.addClass('x-trigger-wrap-focus');
39775             this.mimicing = true;
39776             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39777             if(this.monitorTab){
39778                 this.el.on("keydown", this.checkTab, this);
39779             }
39780         }
39781     },
39782
39783     // private
39784     checkTab : function(e){
39785         if(e.getKey() == e.TAB){
39786             this.triggerBlur();
39787         }
39788     },
39789
39790     // private
39791     onBlur : function(){
39792         // do nothing
39793     },
39794
39795     // private
39796     mimicBlur : function(e, t){
39797         if(!this.wrap.contains(t) && this.validateBlur()){
39798             this.triggerBlur();
39799         }
39800     },
39801
39802     // private
39803     triggerBlur : function(){
39804         this.mimicing = false;
39805         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39806         if(this.monitorTab){
39807             this.el.un("keydown", this.checkTab, this);
39808         }
39809         this.wrap.removeClass('x-trigger-wrap-focus');
39810         Roo.form.TriggerField.superclass.onBlur.call(this);
39811     },
39812
39813     // private
39814     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39815     validateBlur : function(e, t){
39816         return true;
39817     },
39818
39819     // private
39820     onDisable : function(){
39821         Roo.form.TriggerField.superclass.onDisable.call(this);
39822         if(this.wrap){
39823             this.wrap.addClass('x-item-disabled');
39824         }
39825     },
39826
39827     // private
39828     onEnable : function(){
39829         Roo.form.TriggerField.superclass.onEnable.call(this);
39830         if(this.wrap){
39831             this.wrap.removeClass('x-item-disabled');
39832         }
39833     },
39834
39835     // private
39836     onShow : function(){
39837         var ae = this.getActionEl();
39838         
39839         if(ae){
39840             ae.dom.style.display = '';
39841             ae.dom.style.visibility = 'visible';
39842         }
39843     },
39844
39845     // private
39846     
39847     onHide : function(){
39848         var ae = this.getActionEl();
39849         ae.dom.style.display = 'none';
39850     },
39851
39852     /**
39853      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39854      * by an implementing function.
39855      * @method
39856      * @param {EventObject} e
39857      */
39858     onTriggerClick : Roo.emptyFn
39859 });
39860
39861 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39862 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39863 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39864 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39865     initComponent : function(){
39866         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39867
39868         this.triggerConfig = {
39869             tag:'span', cls:'x-form-twin-triggers', cn:[
39870             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39871             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39872         ]};
39873     },
39874
39875     getTrigger : function(index){
39876         return this.triggers[index];
39877     },
39878
39879     initTrigger : function(){
39880         var ts = this.trigger.select('.x-form-trigger', true);
39881         this.wrap.setStyle('overflow', 'hidden');
39882         var triggerField = this;
39883         ts.each(function(t, all, index){
39884             t.hide = function(){
39885                 var w = triggerField.wrap.getWidth();
39886                 this.dom.style.display = 'none';
39887                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39888             };
39889             t.show = function(){
39890                 var w = triggerField.wrap.getWidth();
39891                 this.dom.style.display = '';
39892                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39893             };
39894             var triggerIndex = 'Trigger'+(index+1);
39895
39896             if(this['hide'+triggerIndex]){
39897                 t.dom.style.display = 'none';
39898             }
39899             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
39900             t.addClassOnOver('x-form-trigger-over');
39901             t.addClassOnClick('x-form-trigger-click');
39902         }, this);
39903         this.triggers = ts.elements;
39904     },
39905
39906     onTrigger1Click : Roo.emptyFn,
39907     onTrigger2Click : Roo.emptyFn
39908 });/*
39909  * Based on:
39910  * Ext JS Library 1.1.1
39911  * Copyright(c) 2006-2007, Ext JS, LLC.
39912  *
39913  * Originally Released Under LGPL - original licence link has changed is not relivant.
39914  *
39915  * Fork - LGPL
39916  * <script type="text/javascript">
39917  */
39918  
39919 /**
39920  * @class Roo.form.TextArea
39921  * @extends Roo.form.TextField
39922  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
39923  * support for auto-sizing.
39924  * @constructor
39925  * Creates a new TextArea
39926  * @param {Object} config Configuration options
39927  */
39928 Roo.form.TextArea = function(config){
39929     Roo.form.TextArea.superclass.constructor.call(this, config);
39930     // these are provided exchanges for backwards compat
39931     // minHeight/maxHeight were replaced by growMin/growMax to be
39932     // compatible with TextField growing config values
39933     if(this.minHeight !== undefined){
39934         this.growMin = this.minHeight;
39935     }
39936     if(this.maxHeight !== undefined){
39937         this.growMax = this.maxHeight;
39938     }
39939 };
39940
39941 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
39942     /**
39943      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
39944      */
39945     growMin : 60,
39946     /**
39947      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
39948      */
39949     growMax: 1000,
39950     /**
39951      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
39952      * in the field (equivalent to setting overflow: hidden, defaults to false)
39953      */
39954     preventScrollbars: false,
39955     /**
39956      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39957      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
39958      */
39959
39960     // private
39961     onRender : function(ct, position){
39962         if(!this.el){
39963             this.defaultAutoCreate = {
39964                 tag: "textarea",
39965                 style:"width:300px;height:60px;",
39966                 autocomplete: "new-password"
39967             };
39968         }
39969         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
39970         if(this.grow){
39971             this.textSizeEl = Roo.DomHelper.append(document.body, {
39972                 tag: "pre", cls: "x-form-grow-sizer"
39973             });
39974             if(this.preventScrollbars){
39975                 this.el.setStyle("overflow", "hidden");
39976             }
39977             this.el.setHeight(this.growMin);
39978         }
39979     },
39980
39981     onDestroy : function(){
39982         if(this.textSizeEl){
39983             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
39984         }
39985         Roo.form.TextArea.superclass.onDestroy.call(this);
39986     },
39987
39988     // private
39989     onKeyUp : function(e){
39990         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
39991             this.autoSize();
39992         }
39993     },
39994
39995     /**
39996      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
39997      * This only takes effect if grow = true, and fires the autosize event if the height changes.
39998      */
39999     autoSize : function(){
40000         if(!this.grow || !this.textSizeEl){
40001             return;
40002         }
40003         var el = this.el;
40004         var v = el.dom.value;
40005         var ts = this.textSizeEl;
40006
40007         ts.innerHTML = '';
40008         ts.appendChild(document.createTextNode(v));
40009         v = ts.innerHTML;
40010
40011         Roo.fly(ts).setWidth(this.el.getWidth());
40012         if(v.length < 1){
40013             v = "&#160;&#160;";
40014         }else{
40015             if(Roo.isIE){
40016                 v = v.replace(/\n/g, '<p>&#160;</p>');
40017             }
40018             v += "&#160;\n&#160;";
40019         }
40020         ts.innerHTML = v;
40021         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40022         if(h != this.lastHeight){
40023             this.lastHeight = h;
40024             this.el.setHeight(h);
40025             this.fireEvent("autosize", this, h);
40026         }
40027     }
40028 });/*
40029  * Based on:
40030  * Ext JS Library 1.1.1
40031  * Copyright(c) 2006-2007, Ext JS, LLC.
40032  *
40033  * Originally Released Under LGPL - original licence link has changed is not relivant.
40034  *
40035  * Fork - LGPL
40036  * <script type="text/javascript">
40037  */
40038  
40039
40040 /**
40041  * @class Roo.form.NumberField
40042  * @extends Roo.form.TextField
40043  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40044  * @constructor
40045  * Creates a new NumberField
40046  * @param {Object} config Configuration options
40047  */
40048 Roo.form.NumberField = function(config){
40049     Roo.form.NumberField.superclass.constructor.call(this, config);
40050 };
40051
40052 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40053     /**
40054      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40055      */
40056     fieldClass: "x-form-field x-form-num-field",
40057     /**
40058      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40059      */
40060     allowDecimals : true,
40061     /**
40062      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40063      */
40064     decimalSeparator : ".",
40065     /**
40066      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40067      */
40068     decimalPrecision : 2,
40069     /**
40070      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40071      */
40072     allowNegative : true,
40073     /**
40074      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40075      */
40076     minValue : Number.NEGATIVE_INFINITY,
40077     /**
40078      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40079      */
40080     maxValue : Number.MAX_VALUE,
40081     /**
40082      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40083      */
40084     minText : "The minimum value for this field is {0}",
40085     /**
40086      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40087      */
40088     maxText : "The maximum value for this field is {0}",
40089     /**
40090      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40091      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40092      */
40093     nanText : "{0} is not a valid number",
40094
40095     // private
40096     initEvents : function(){
40097         Roo.form.NumberField.superclass.initEvents.call(this);
40098         var allowed = "0123456789";
40099         if(this.allowDecimals){
40100             allowed += this.decimalSeparator;
40101         }
40102         if(this.allowNegative){
40103             allowed += "-";
40104         }
40105         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40106         var keyPress = function(e){
40107             var k = e.getKey();
40108             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40109                 return;
40110             }
40111             var c = e.getCharCode();
40112             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40113                 e.stopEvent();
40114             }
40115         };
40116         this.el.on("keypress", keyPress, this);
40117     },
40118
40119     // private
40120     validateValue : function(value){
40121         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40122             return false;
40123         }
40124         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40125              return true;
40126         }
40127         var num = this.parseValue(value);
40128         if(isNaN(num)){
40129             this.markInvalid(String.format(this.nanText, value));
40130             return false;
40131         }
40132         if(num < this.minValue){
40133             this.markInvalid(String.format(this.minText, this.minValue));
40134             return false;
40135         }
40136         if(num > this.maxValue){
40137             this.markInvalid(String.format(this.maxText, this.maxValue));
40138             return false;
40139         }
40140         return true;
40141     },
40142
40143     getValue : function(){
40144         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40145     },
40146
40147     // private
40148     parseValue : function(value){
40149         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40150         return isNaN(value) ? '' : value;
40151     },
40152
40153     // private
40154     fixPrecision : function(value){
40155         var nan = isNaN(value);
40156         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40157             return nan ? '' : value;
40158         }
40159         return parseFloat(value).toFixed(this.decimalPrecision);
40160     },
40161
40162     setValue : function(v){
40163         v = this.fixPrecision(v);
40164         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40165     },
40166
40167     // private
40168     decimalPrecisionFcn : function(v){
40169         return Math.floor(v);
40170     },
40171
40172     beforeBlur : function(){
40173         var v = this.parseValue(this.getRawValue());
40174         if(v){
40175             this.setValue(v);
40176         }
40177     }
40178 });/*
40179  * Based on:
40180  * Ext JS Library 1.1.1
40181  * Copyright(c) 2006-2007, Ext JS, LLC.
40182  *
40183  * Originally Released Under LGPL - original licence link has changed is not relivant.
40184  *
40185  * Fork - LGPL
40186  * <script type="text/javascript">
40187  */
40188  
40189 /**
40190  * @class Roo.form.DateField
40191  * @extends Roo.form.TriggerField
40192  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40193 * @constructor
40194 * Create a new DateField
40195 * @param {Object} config
40196  */
40197 Roo.form.DateField = function(config){
40198     Roo.form.DateField.superclass.constructor.call(this, config);
40199     
40200       this.addEvents({
40201          
40202         /**
40203          * @event select
40204          * Fires when a date is selected
40205              * @param {Roo.form.DateField} combo This combo box
40206              * @param {Date} date The date selected
40207              */
40208         'select' : true
40209          
40210     });
40211     
40212     
40213     if(typeof this.minValue == "string") {
40214         this.minValue = this.parseDate(this.minValue);
40215     }
40216     if(typeof this.maxValue == "string") {
40217         this.maxValue = this.parseDate(this.maxValue);
40218     }
40219     this.ddMatch = null;
40220     if(this.disabledDates){
40221         var dd = this.disabledDates;
40222         var re = "(?:";
40223         for(var i = 0; i < dd.length; i++){
40224             re += dd[i];
40225             if(i != dd.length-1) {
40226                 re += "|";
40227             }
40228         }
40229         this.ddMatch = new RegExp(re + ")");
40230     }
40231 };
40232
40233 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40234     /**
40235      * @cfg {String} format
40236      * The default date format string which can be overriden for localization support.  The format must be
40237      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40238      */
40239     format : "m/d/y",
40240     /**
40241      * @cfg {String} altFormats
40242      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40243      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40244      */
40245     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40246     /**
40247      * @cfg {Array} disabledDays
40248      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40249      */
40250     disabledDays : null,
40251     /**
40252      * @cfg {String} disabledDaysText
40253      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40254      */
40255     disabledDaysText : "Disabled",
40256     /**
40257      * @cfg {Array} disabledDates
40258      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40259      * expression so they are very powerful. Some examples:
40260      * <ul>
40261      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40262      * <li>["03/08", "09/16"] would disable those days for every year</li>
40263      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40264      * <li>["03/../2006"] would disable every day in March 2006</li>
40265      * <li>["^03"] would disable every day in every March</li>
40266      * </ul>
40267      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40268      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40269      */
40270     disabledDates : null,
40271     /**
40272      * @cfg {String} disabledDatesText
40273      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40274      */
40275     disabledDatesText : "Disabled",
40276     /**
40277      * @cfg {Date/String} minValue
40278      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40279      * valid format (defaults to null).
40280      */
40281     minValue : null,
40282     /**
40283      * @cfg {Date/String} maxValue
40284      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40285      * valid format (defaults to null).
40286      */
40287     maxValue : null,
40288     /**
40289      * @cfg {String} minText
40290      * The error text to display when the date in the cell is before minValue (defaults to
40291      * 'The date in this field must be after {minValue}').
40292      */
40293     minText : "The date in this field must be equal to or after {0}",
40294     /**
40295      * @cfg {String} maxText
40296      * The error text to display when the date in the cell is after maxValue (defaults to
40297      * 'The date in this field must be before {maxValue}').
40298      */
40299     maxText : "The date in this field must be equal to or before {0}",
40300     /**
40301      * @cfg {String} invalidText
40302      * The error text to display when the date in the field is invalid (defaults to
40303      * '{value} is not a valid date - it must be in the format {format}').
40304      */
40305     invalidText : "{0} is not a valid date - it must be in the format {1}",
40306     /**
40307      * @cfg {String} triggerClass
40308      * An additional CSS class used to style the trigger button.  The trigger will always get the
40309      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40310      * which displays a calendar icon).
40311      */
40312     triggerClass : 'x-form-date-trigger',
40313     
40314
40315     /**
40316      * @cfg {Boolean} useIso
40317      * if enabled, then the date field will use a hidden field to store the 
40318      * real value as iso formated date. default (false)
40319      */ 
40320     useIso : false,
40321     /**
40322      * @cfg {String/Object} autoCreate
40323      * A DomHelper element spec, or true for a default element spec (defaults to
40324      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40325      */ 
40326     // private
40327     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40328     
40329     // private
40330     hiddenField: false,
40331     
40332     onRender : function(ct, position)
40333     {
40334         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40335         if (this.useIso) {
40336             //this.el.dom.removeAttribute('name'); 
40337             Roo.log("Changing name?");
40338             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40339             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40340                     'before', true);
40341             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40342             // prevent input submission
40343             this.hiddenName = this.name;
40344         }
40345             
40346             
40347     },
40348     
40349     // private
40350     validateValue : function(value)
40351     {
40352         value = this.formatDate(value);
40353         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40354             Roo.log('super failed');
40355             return false;
40356         }
40357         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40358              return true;
40359         }
40360         var svalue = value;
40361         value = this.parseDate(value);
40362         if(!value){
40363             Roo.log('parse date failed' + svalue);
40364             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40365             return false;
40366         }
40367         var time = value.getTime();
40368         if(this.minValue && time < this.minValue.getTime()){
40369             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40370             return false;
40371         }
40372         if(this.maxValue && time > this.maxValue.getTime()){
40373             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40374             return false;
40375         }
40376         if(this.disabledDays){
40377             var day = value.getDay();
40378             for(var i = 0; i < this.disabledDays.length; i++) {
40379                 if(day === this.disabledDays[i]){
40380                     this.markInvalid(this.disabledDaysText);
40381                     return false;
40382                 }
40383             }
40384         }
40385         var fvalue = this.formatDate(value);
40386         if(this.ddMatch && this.ddMatch.test(fvalue)){
40387             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40388             return false;
40389         }
40390         return true;
40391     },
40392
40393     // private
40394     // Provides logic to override the default TriggerField.validateBlur which just returns true
40395     validateBlur : function(){
40396         return !this.menu || !this.menu.isVisible();
40397     },
40398     
40399     getName: function()
40400     {
40401         // returns hidden if it's set..
40402         if (!this.rendered) {return ''};
40403         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40404         
40405     },
40406
40407     /**
40408      * Returns the current date value of the date field.
40409      * @return {Date} The date value
40410      */
40411     getValue : function(){
40412         
40413         return  this.hiddenField ?
40414                 this.hiddenField.value :
40415                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40416     },
40417
40418     /**
40419      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40420      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40421      * (the default format used is "m/d/y").
40422      * <br />Usage:
40423      * <pre><code>
40424 //All of these calls set the same date value (May 4, 2006)
40425
40426 //Pass a date object:
40427 var dt = new Date('5/4/06');
40428 dateField.setValue(dt);
40429
40430 //Pass a date string (default format):
40431 dateField.setValue('5/4/06');
40432
40433 //Pass a date string (custom format):
40434 dateField.format = 'Y-m-d';
40435 dateField.setValue('2006-5-4');
40436 </code></pre>
40437      * @param {String/Date} date The date or valid date string
40438      */
40439     setValue : function(date){
40440         if (this.hiddenField) {
40441             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40442         }
40443         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40444         // make sure the value field is always stored as a date..
40445         this.value = this.parseDate(date);
40446         
40447         
40448     },
40449
40450     // private
40451     parseDate : function(value){
40452         if(!value || value instanceof Date){
40453             return value;
40454         }
40455         var v = Date.parseDate(value, this.format);
40456          if (!v && this.useIso) {
40457             v = Date.parseDate(value, 'Y-m-d');
40458         }
40459         if(!v && this.altFormats){
40460             if(!this.altFormatsArray){
40461                 this.altFormatsArray = this.altFormats.split("|");
40462             }
40463             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40464                 v = Date.parseDate(value, this.altFormatsArray[i]);
40465             }
40466         }
40467         return v;
40468     },
40469
40470     // private
40471     formatDate : function(date, fmt){
40472         return (!date || !(date instanceof Date)) ?
40473                date : date.dateFormat(fmt || this.format);
40474     },
40475
40476     // private
40477     menuListeners : {
40478         select: function(m, d){
40479             
40480             this.setValue(d);
40481             this.fireEvent('select', this, d);
40482         },
40483         show : function(){ // retain focus styling
40484             this.onFocus();
40485         },
40486         hide : function(){
40487             this.focus.defer(10, this);
40488             var ml = this.menuListeners;
40489             this.menu.un("select", ml.select,  this);
40490             this.menu.un("show", ml.show,  this);
40491             this.menu.un("hide", ml.hide,  this);
40492         }
40493     },
40494
40495     // private
40496     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40497     onTriggerClick : function(){
40498         if(this.disabled){
40499             return;
40500         }
40501         if(this.menu == null){
40502             this.menu = new Roo.menu.DateMenu();
40503         }
40504         Roo.apply(this.menu.picker,  {
40505             showClear: this.allowBlank,
40506             minDate : this.minValue,
40507             maxDate : this.maxValue,
40508             disabledDatesRE : this.ddMatch,
40509             disabledDatesText : this.disabledDatesText,
40510             disabledDays : this.disabledDays,
40511             disabledDaysText : this.disabledDaysText,
40512             format : this.useIso ? 'Y-m-d' : this.format,
40513             minText : String.format(this.minText, this.formatDate(this.minValue)),
40514             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40515         });
40516         this.menu.on(Roo.apply({}, this.menuListeners, {
40517             scope:this
40518         }));
40519         this.menu.picker.setValue(this.getValue() || new Date());
40520         this.menu.show(this.el, "tl-bl?");
40521     },
40522
40523     beforeBlur : function(){
40524         var v = this.parseDate(this.getRawValue());
40525         if(v){
40526             this.setValue(v);
40527         }
40528     },
40529
40530     /*@
40531      * overide
40532      * 
40533      */
40534     isDirty : function() {
40535         if(this.disabled) {
40536             return false;
40537         }
40538         
40539         if(typeof(this.startValue) === 'undefined'){
40540             return false;
40541         }
40542         
40543         return String(this.getValue()) !== String(this.startValue);
40544         
40545     }
40546 });/*
40547  * Based on:
40548  * Ext JS Library 1.1.1
40549  * Copyright(c) 2006-2007, Ext JS, LLC.
40550  *
40551  * Originally Released Under LGPL - original licence link has changed is not relivant.
40552  *
40553  * Fork - LGPL
40554  * <script type="text/javascript">
40555  */
40556  
40557 /**
40558  * @class Roo.form.MonthField
40559  * @extends Roo.form.TriggerField
40560  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40561 * @constructor
40562 * Create a new MonthField
40563 * @param {Object} config
40564  */
40565 Roo.form.MonthField = function(config){
40566     
40567     Roo.form.MonthField.superclass.constructor.call(this, config);
40568     
40569       this.addEvents({
40570          
40571         /**
40572          * @event select
40573          * Fires when a date is selected
40574              * @param {Roo.form.MonthFieeld} combo This combo box
40575              * @param {Date} date The date selected
40576              */
40577         'select' : true
40578          
40579     });
40580     
40581     
40582     if(typeof this.minValue == "string") {
40583         this.minValue = this.parseDate(this.minValue);
40584     }
40585     if(typeof this.maxValue == "string") {
40586         this.maxValue = this.parseDate(this.maxValue);
40587     }
40588     this.ddMatch = null;
40589     if(this.disabledDates){
40590         var dd = this.disabledDates;
40591         var re = "(?:";
40592         for(var i = 0; i < dd.length; i++){
40593             re += dd[i];
40594             if(i != dd.length-1) {
40595                 re += "|";
40596             }
40597         }
40598         this.ddMatch = new RegExp(re + ")");
40599     }
40600 };
40601
40602 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40603     /**
40604      * @cfg {String} format
40605      * The default date format string which can be overriden for localization support.  The format must be
40606      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40607      */
40608     format : "M Y",
40609     /**
40610      * @cfg {String} altFormats
40611      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40612      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40613      */
40614     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40615     /**
40616      * @cfg {Array} disabledDays
40617      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40618      */
40619     disabledDays : [0,1,2,3,4,5,6],
40620     /**
40621      * @cfg {String} disabledDaysText
40622      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40623      */
40624     disabledDaysText : "Disabled",
40625     /**
40626      * @cfg {Array} disabledDates
40627      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40628      * expression so they are very powerful. Some examples:
40629      * <ul>
40630      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40631      * <li>["03/08", "09/16"] would disable those days for every year</li>
40632      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40633      * <li>["03/../2006"] would disable every day in March 2006</li>
40634      * <li>["^03"] would disable every day in every March</li>
40635      * </ul>
40636      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40637      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40638      */
40639     disabledDates : null,
40640     /**
40641      * @cfg {String} disabledDatesText
40642      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40643      */
40644     disabledDatesText : "Disabled",
40645     /**
40646      * @cfg {Date/String} minValue
40647      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40648      * valid format (defaults to null).
40649      */
40650     minValue : null,
40651     /**
40652      * @cfg {Date/String} maxValue
40653      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40654      * valid format (defaults to null).
40655      */
40656     maxValue : null,
40657     /**
40658      * @cfg {String} minText
40659      * The error text to display when the date in the cell is before minValue (defaults to
40660      * 'The date in this field must be after {minValue}').
40661      */
40662     minText : "The date in this field must be equal to or after {0}",
40663     /**
40664      * @cfg {String} maxTextf
40665      * The error text to display when the date in the cell is after maxValue (defaults to
40666      * 'The date in this field must be before {maxValue}').
40667      */
40668     maxText : "The date in this field must be equal to or before {0}",
40669     /**
40670      * @cfg {String} invalidText
40671      * The error text to display when the date in the field is invalid (defaults to
40672      * '{value} is not a valid date - it must be in the format {format}').
40673      */
40674     invalidText : "{0} is not a valid date - it must be in the format {1}",
40675     /**
40676      * @cfg {String} triggerClass
40677      * An additional CSS class used to style the trigger button.  The trigger will always get the
40678      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40679      * which displays a calendar icon).
40680      */
40681     triggerClass : 'x-form-date-trigger',
40682     
40683
40684     /**
40685      * @cfg {Boolean} useIso
40686      * if enabled, then the date field will use a hidden field to store the 
40687      * real value as iso formated date. default (true)
40688      */ 
40689     useIso : true,
40690     /**
40691      * @cfg {String/Object} autoCreate
40692      * A DomHelper element spec, or true for a default element spec (defaults to
40693      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40694      */ 
40695     // private
40696     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40697     
40698     // private
40699     hiddenField: false,
40700     
40701     hideMonthPicker : false,
40702     
40703     onRender : function(ct, position)
40704     {
40705         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40706         if (this.useIso) {
40707             this.el.dom.removeAttribute('name'); 
40708             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40709                     'before', true);
40710             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40711             // prevent input submission
40712             this.hiddenName = this.name;
40713         }
40714             
40715             
40716     },
40717     
40718     // private
40719     validateValue : function(value)
40720     {
40721         value = this.formatDate(value);
40722         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40723             return false;
40724         }
40725         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40726              return true;
40727         }
40728         var svalue = value;
40729         value = this.parseDate(value);
40730         if(!value){
40731             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40732             return false;
40733         }
40734         var time = value.getTime();
40735         if(this.minValue && time < this.minValue.getTime()){
40736             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40737             return false;
40738         }
40739         if(this.maxValue && time > this.maxValue.getTime()){
40740             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40741             return false;
40742         }
40743         /*if(this.disabledDays){
40744             var day = value.getDay();
40745             for(var i = 0; i < this.disabledDays.length; i++) {
40746                 if(day === this.disabledDays[i]){
40747                     this.markInvalid(this.disabledDaysText);
40748                     return false;
40749                 }
40750             }
40751         }
40752         */
40753         var fvalue = this.formatDate(value);
40754         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40755             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40756             return false;
40757         }
40758         */
40759         return true;
40760     },
40761
40762     // private
40763     // Provides logic to override the default TriggerField.validateBlur which just returns true
40764     validateBlur : function(){
40765         return !this.menu || !this.menu.isVisible();
40766     },
40767
40768     /**
40769      * Returns the current date value of the date field.
40770      * @return {Date} The date value
40771      */
40772     getValue : function(){
40773         
40774         
40775         
40776         return  this.hiddenField ?
40777                 this.hiddenField.value :
40778                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40779     },
40780
40781     /**
40782      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40783      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40784      * (the default format used is "m/d/y").
40785      * <br />Usage:
40786      * <pre><code>
40787 //All of these calls set the same date value (May 4, 2006)
40788
40789 //Pass a date object:
40790 var dt = new Date('5/4/06');
40791 monthField.setValue(dt);
40792
40793 //Pass a date string (default format):
40794 monthField.setValue('5/4/06');
40795
40796 //Pass a date string (custom format):
40797 monthField.format = 'Y-m-d';
40798 monthField.setValue('2006-5-4');
40799 </code></pre>
40800      * @param {String/Date} date The date or valid date string
40801      */
40802     setValue : function(date){
40803         Roo.log('month setValue' + date);
40804         // can only be first of month..
40805         
40806         var val = this.parseDate(date);
40807         
40808         if (this.hiddenField) {
40809             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40810         }
40811         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40812         this.value = this.parseDate(date);
40813     },
40814
40815     // private
40816     parseDate : function(value){
40817         if(!value || value instanceof Date){
40818             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40819             return value;
40820         }
40821         var v = Date.parseDate(value, this.format);
40822         if (!v && this.useIso) {
40823             v = Date.parseDate(value, 'Y-m-d');
40824         }
40825         if (v) {
40826             // 
40827             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40828         }
40829         
40830         
40831         if(!v && this.altFormats){
40832             if(!this.altFormatsArray){
40833                 this.altFormatsArray = this.altFormats.split("|");
40834             }
40835             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40836                 v = Date.parseDate(value, this.altFormatsArray[i]);
40837             }
40838         }
40839         return v;
40840     },
40841
40842     // private
40843     formatDate : function(date, fmt){
40844         return (!date || !(date instanceof Date)) ?
40845                date : date.dateFormat(fmt || this.format);
40846     },
40847
40848     // private
40849     menuListeners : {
40850         select: function(m, d){
40851             this.setValue(d);
40852             this.fireEvent('select', this, d);
40853         },
40854         show : function(){ // retain focus styling
40855             this.onFocus();
40856         },
40857         hide : function(){
40858             this.focus.defer(10, this);
40859             var ml = this.menuListeners;
40860             this.menu.un("select", ml.select,  this);
40861             this.menu.un("show", ml.show,  this);
40862             this.menu.un("hide", ml.hide,  this);
40863         }
40864     },
40865     // private
40866     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40867     onTriggerClick : function(){
40868         if(this.disabled){
40869             return;
40870         }
40871         if(this.menu == null){
40872             this.menu = new Roo.menu.DateMenu();
40873            
40874         }
40875         
40876         Roo.apply(this.menu.picker,  {
40877             
40878             showClear: this.allowBlank,
40879             minDate : this.minValue,
40880             maxDate : this.maxValue,
40881             disabledDatesRE : this.ddMatch,
40882             disabledDatesText : this.disabledDatesText,
40883             
40884             format : this.useIso ? 'Y-m-d' : this.format,
40885             minText : String.format(this.minText, this.formatDate(this.minValue)),
40886             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40887             
40888         });
40889          this.menu.on(Roo.apply({}, this.menuListeners, {
40890             scope:this
40891         }));
40892        
40893         
40894         var m = this.menu;
40895         var p = m.picker;
40896         
40897         // hide month picker get's called when we called by 'before hide';
40898         
40899         var ignorehide = true;
40900         p.hideMonthPicker  = function(disableAnim){
40901             if (ignorehide) {
40902                 return;
40903             }
40904              if(this.monthPicker){
40905                 Roo.log("hideMonthPicker called");
40906                 if(disableAnim === true){
40907                     this.monthPicker.hide();
40908                 }else{
40909                     this.monthPicker.slideOut('t', {duration:.2});
40910                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
40911                     p.fireEvent("select", this, this.value);
40912                     m.hide();
40913                 }
40914             }
40915         }
40916         
40917         Roo.log('picker set value');
40918         Roo.log(this.getValue());
40919         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
40920         m.show(this.el, 'tl-bl?');
40921         ignorehide  = false;
40922         // this will trigger hideMonthPicker..
40923         
40924         
40925         // hidden the day picker
40926         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
40927         
40928         
40929         
40930       
40931         
40932         p.showMonthPicker.defer(100, p);
40933     
40934         
40935        
40936     },
40937
40938     beforeBlur : function(){
40939         var v = this.parseDate(this.getRawValue());
40940         if(v){
40941             this.setValue(v);
40942         }
40943     }
40944
40945     /** @cfg {Boolean} grow @hide */
40946     /** @cfg {Number} growMin @hide */
40947     /** @cfg {Number} growMax @hide */
40948     /**
40949      * @hide
40950      * @method autoSize
40951      */
40952 });/*
40953  * Based on:
40954  * Ext JS Library 1.1.1
40955  * Copyright(c) 2006-2007, Ext JS, LLC.
40956  *
40957  * Originally Released Under LGPL - original licence link has changed is not relivant.
40958  *
40959  * Fork - LGPL
40960  * <script type="text/javascript">
40961  */
40962  
40963
40964 /**
40965  * @class Roo.form.ComboBox
40966  * @extends Roo.form.TriggerField
40967  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
40968  * @constructor
40969  * Create a new ComboBox.
40970  * @param {Object} config Configuration options
40971  */
40972 Roo.form.ComboBox = function(config){
40973     Roo.form.ComboBox.superclass.constructor.call(this, config);
40974     this.addEvents({
40975         /**
40976          * @event expand
40977          * Fires when the dropdown list is expanded
40978              * @param {Roo.form.ComboBox} combo This combo box
40979              */
40980         'expand' : true,
40981         /**
40982          * @event collapse
40983          * Fires when the dropdown list is collapsed
40984              * @param {Roo.form.ComboBox} combo This combo box
40985              */
40986         'collapse' : true,
40987         /**
40988          * @event beforeselect
40989          * Fires before a list item is selected. Return false to cancel the selection.
40990              * @param {Roo.form.ComboBox} combo This combo box
40991              * @param {Roo.data.Record} record The data record returned from the underlying store
40992              * @param {Number} index The index of the selected item in the dropdown list
40993              */
40994         'beforeselect' : true,
40995         /**
40996          * @event select
40997          * Fires when a list item is selected
40998              * @param {Roo.form.ComboBox} combo This combo box
40999              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41000              * @param {Number} index The index of the selected item in the dropdown list
41001              */
41002         'select' : true,
41003         /**
41004          * @event beforequery
41005          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41006          * The event object passed has these properties:
41007              * @param {Roo.form.ComboBox} combo This combo box
41008              * @param {String} query The query
41009              * @param {Boolean} forceAll true to force "all" query
41010              * @param {Boolean} cancel true to cancel the query
41011              * @param {Object} e The query event object
41012              */
41013         'beforequery': true,
41014          /**
41015          * @event add
41016          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41017              * @param {Roo.form.ComboBox} combo This combo box
41018              */
41019         'add' : true,
41020         /**
41021          * @event edit
41022          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41023              * @param {Roo.form.ComboBox} combo This combo box
41024              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41025              */
41026         'edit' : true
41027         
41028         
41029     });
41030     if(this.transform){
41031         this.allowDomMove = false;
41032         var s = Roo.getDom(this.transform);
41033         if(!this.hiddenName){
41034             this.hiddenName = s.name;
41035         }
41036         if(!this.store){
41037             this.mode = 'local';
41038             var d = [], opts = s.options;
41039             for(var i = 0, len = opts.length;i < len; i++){
41040                 var o = opts[i];
41041                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41042                 if(o.selected) {
41043                     this.value = value;
41044                 }
41045                 d.push([value, o.text]);
41046             }
41047             this.store = new Roo.data.SimpleStore({
41048                 'id': 0,
41049                 fields: ['value', 'text'],
41050                 data : d
41051             });
41052             this.valueField = 'value';
41053             this.displayField = 'text';
41054         }
41055         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41056         if(!this.lazyRender){
41057             this.target = true;
41058             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41059             s.parentNode.removeChild(s); // remove it
41060             this.render(this.el.parentNode);
41061         }else{
41062             s.parentNode.removeChild(s); // remove it
41063         }
41064
41065     }
41066     if (this.store) {
41067         this.store = Roo.factory(this.store, Roo.data);
41068     }
41069     
41070     this.selectedIndex = -1;
41071     if(this.mode == 'local'){
41072         if(config.queryDelay === undefined){
41073             this.queryDelay = 10;
41074         }
41075         if(config.minChars === undefined){
41076             this.minChars = 0;
41077         }
41078     }
41079 };
41080
41081 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41082     /**
41083      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41084      */
41085     /**
41086      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41087      * rendering into an Roo.Editor, defaults to false)
41088      */
41089     /**
41090      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41091      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41092      */
41093     /**
41094      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41095      */
41096     /**
41097      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41098      * the dropdown list (defaults to undefined, with no header element)
41099      */
41100
41101      /**
41102      * @cfg {String/Roo.Template} tpl The template to use to render the output
41103      */
41104      
41105     // private
41106     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41107     /**
41108      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41109      */
41110     listWidth: undefined,
41111     /**
41112      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41113      * mode = 'remote' or 'text' if mode = 'local')
41114      */
41115     displayField: undefined,
41116     /**
41117      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41118      * mode = 'remote' or 'value' if mode = 'local'). 
41119      * Note: use of a valueField requires the user make a selection
41120      * in order for a value to be mapped.
41121      */
41122     valueField: undefined,
41123     
41124     
41125     /**
41126      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41127      * field's data value (defaults to the underlying DOM element's name)
41128      */
41129     hiddenName: undefined,
41130     /**
41131      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41132      */
41133     listClass: '',
41134     /**
41135      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41136      */
41137     selectedClass: 'x-combo-selected',
41138     /**
41139      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41140      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41141      * which displays a downward arrow icon).
41142      */
41143     triggerClass : 'x-form-arrow-trigger',
41144     /**
41145      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41146      */
41147     shadow:'sides',
41148     /**
41149      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41150      * anchor positions (defaults to 'tl-bl')
41151      */
41152     listAlign: 'tl-bl?',
41153     /**
41154      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41155      */
41156     maxHeight: 300,
41157     /**
41158      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41159      * query specified by the allQuery config option (defaults to 'query')
41160      */
41161     triggerAction: 'query',
41162     /**
41163      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41164      * (defaults to 4, does not apply if editable = false)
41165      */
41166     minChars : 4,
41167     /**
41168      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41169      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41170      */
41171     typeAhead: false,
41172     /**
41173      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41174      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41175      */
41176     queryDelay: 500,
41177     /**
41178      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41179      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41180      */
41181     pageSize: 0,
41182     /**
41183      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41184      * when editable = true (defaults to false)
41185      */
41186     selectOnFocus:false,
41187     /**
41188      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41189      */
41190     queryParam: 'query',
41191     /**
41192      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41193      * when mode = 'remote' (defaults to 'Loading...')
41194      */
41195     loadingText: 'Loading...',
41196     /**
41197      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41198      */
41199     resizable: false,
41200     /**
41201      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41202      */
41203     handleHeight : 8,
41204     /**
41205      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41206      * traditional select (defaults to true)
41207      */
41208     editable: true,
41209     /**
41210      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41211      */
41212     allQuery: '',
41213     /**
41214      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41215      */
41216     mode: 'remote',
41217     /**
41218      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41219      * listWidth has a higher value)
41220      */
41221     minListWidth : 70,
41222     /**
41223      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41224      * allow the user to set arbitrary text into the field (defaults to false)
41225      */
41226     forceSelection:false,
41227     /**
41228      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41229      * if typeAhead = true (defaults to 250)
41230      */
41231     typeAheadDelay : 250,
41232     /**
41233      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41234      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41235      */
41236     valueNotFoundText : undefined,
41237     /**
41238      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41239      */
41240     blockFocus : false,
41241     
41242     /**
41243      * @cfg {Boolean} disableClear Disable showing of clear button.
41244      */
41245     disableClear : false,
41246     /**
41247      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41248      */
41249     alwaysQuery : false,
41250     
41251     //private
41252     addicon : false,
41253     editicon: false,
41254     
41255     // element that contains real text value.. (when hidden is used..)
41256      
41257     // private
41258     onRender : function(ct, position){
41259         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41260         if(this.hiddenName){
41261             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41262                     'before', true);
41263             this.hiddenField.value =
41264                 this.hiddenValue !== undefined ? this.hiddenValue :
41265                 this.value !== undefined ? this.value : '';
41266
41267             // prevent input submission
41268             this.el.dom.removeAttribute('name');
41269              
41270              
41271         }
41272         if(Roo.isGecko){
41273             this.el.dom.setAttribute('autocomplete', 'off');
41274         }
41275
41276         var cls = 'x-combo-list';
41277
41278         this.list = new Roo.Layer({
41279             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41280         });
41281
41282         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41283         this.list.setWidth(lw);
41284         this.list.swallowEvent('mousewheel');
41285         this.assetHeight = 0;
41286
41287         if(this.title){
41288             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41289             this.assetHeight += this.header.getHeight();
41290         }
41291
41292         this.innerList = this.list.createChild({cls:cls+'-inner'});
41293         this.innerList.on('mouseover', this.onViewOver, this);
41294         this.innerList.on('mousemove', this.onViewMove, this);
41295         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41296         
41297         if(this.allowBlank && !this.pageSize && !this.disableClear){
41298             this.footer = this.list.createChild({cls:cls+'-ft'});
41299             this.pageTb = new Roo.Toolbar(this.footer);
41300            
41301         }
41302         if(this.pageSize){
41303             this.footer = this.list.createChild({cls:cls+'-ft'});
41304             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41305                     {pageSize: this.pageSize});
41306             
41307         }
41308         
41309         if (this.pageTb && this.allowBlank && !this.disableClear) {
41310             var _this = this;
41311             this.pageTb.add(new Roo.Toolbar.Fill(), {
41312                 cls: 'x-btn-icon x-btn-clear',
41313                 text: '&#160;',
41314                 handler: function()
41315                 {
41316                     _this.collapse();
41317                     _this.clearValue();
41318                     _this.onSelect(false, -1);
41319                 }
41320             });
41321         }
41322         if (this.footer) {
41323             this.assetHeight += this.footer.getHeight();
41324         }
41325         
41326
41327         if(!this.tpl){
41328             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41329         }
41330
41331         this.view = new Roo.View(this.innerList, this.tpl, {
41332             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41333         });
41334
41335         this.view.on('click', this.onViewClick, this);
41336
41337         this.store.on('beforeload', this.onBeforeLoad, this);
41338         this.store.on('load', this.onLoad, this);
41339         this.store.on('loadexception', this.onLoadException, this);
41340
41341         if(this.resizable){
41342             this.resizer = new Roo.Resizable(this.list,  {
41343                pinned:true, handles:'se'
41344             });
41345             this.resizer.on('resize', function(r, w, h){
41346                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41347                 this.listWidth = w;
41348                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41349                 this.restrictHeight();
41350             }, this);
41351             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41352         }
41353         if(!this.editable){
41354             this.editable = true;
41355             this.setEditable(false);
41356         }  
41357         
41358         
41359         if (typeof(this.events.add.listeners) != 'undefined') {
41360             
41361             this.addicon = this.wrap.createChild(
41362                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41363        
41364             this.addicon.on('click', function(e) {
41365                 this.fireEvent('add', this);
41366             }, this);
41367         }
41368         if (typeof(this.events.edit.listeners) != 'undefined') {
41369             
41370             this.editicon = this.wrap.createChild(
41371                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41372             if (this.addicon) {
41373                 this.editicon.setStyle('margin-left', '40px');
41374             }
41375             this.editicon.on('click', function(e) {
41376                 
41377                 // we fire even  if inothing is selected..
41378                 this.fireEvent('edit', this, this.lastData );
41379                 
41380             }, this);
41381         }
41382         
41383         
41384         
41385     },
41386
41387     // private
41388     initEvents : function(){
41389         Roo.form.ComboBox.superclass.initEvents.call(this);
41390
41391         this.keyNav = new Roo.KeyNav(this.el, {
41392             "up" : function(e){
41393                 this.inKeyMode = true;
41394                 this.selectPrev();
41395             },
41396
41397             "down" : function(e){
41398                 if(!this.isExpanded()){
41399                     this.onTriggerClick();
41400                 }else{
41401                     this.inKeyMode = true;
41402                     this.selectNext();
41403                 }
41404             },
41405
41406             "enter" : function(e){
41407                 this.onViewClick();
41408                 //return true;
41409             },
41410
41411             "esc" : function(e){
41412                 this.collapse();
41413             },
41414
41415             "tab" : function(e){
41416                 this.onViewClick(false);
41417                 this.fireEvent("specialkey", this, e);
41418                 return true;
41419             },
41420
41421             scope : this,
41422
41423             doRelay : function(foo, bar, hname){
41424                 if(hname == 'down' || this.scope.isExpanded()){
41425                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41426                 }
41427                 return true;
41428             },
41429
41430             forceKeyDown: true
41431         });
41432         this.queryDelay = Math.max(this.queryDelay || 10,
41433                 this.mode == 'local' ? 10 : 250);
41434         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41435         if(this.typeAhead){
41436             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41437         }
41438         if(this.editable !== false){
41439             this.el.on("keyup", this.onKeyUp, this);
41440         }
41441         if(this.forceSelection){
41442             this.on('blur', this.doForce, this);
41443         }
41444     },
41445
41446     onDestroy : function(){
41447         if(this.view){
41448             this.view.setStore(null);
41449             this.view.el.removeAllListeners();
41450             this.view.el.remove();
41451             this.view.purgeListeners();
41452         }
41453         if(this.list){
41454             this.list.destroy();
41455         }
41456         if(this.store){
41457             this.store.un('beforeload', this.onBeforeLoad, this);
41458             this.store.un('load', this.onLoad, this);
41459             this.store.un('loadexception', this.onLoadException, this);
41460         }
41461         Roo.form.ComboBox.superclass.onDestroy.call(this);
41462     },
41463
41464     // private
41465     fireKey : function(e){
41466         if(e.isNavKeyPress() && !this.list.isVisible()){
41467             this.fireEvent("specialkey", this, e);
41468         }
41469     },
41470
41471     // private
41472     onResize: function(w, h){
41473         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41474         
41475         if(typeof w != 'number'){
41476             // we do not handle it!?!?
41477             return;
41478         }
41479         var tw = this.trigger.getWidth();
41480         tw += this.addicon ? this.addicon.getWidth() : 0;
41481         tw += this.editicon ? this.editicon.getWidth() : 0;
41482         var x = w - tw;
41483         this.el.setWidth( this.adjustWidth('input', x));
41484             
41485         this.trigger.setStyle('left', x+'px');
41486         
41487         if(this.list && this.listWidth === undefined){
41488             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41489             this.list.setWidth(lw);
41490             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41491         }
41492         
41493     
41494         
41495     },
41496
41497     /**
41498      * Allow or prevent the user from directly editing the field text.  If false is passed,
41499      * the user will only be able to select from the items defined in the dropdown list.  This method
41500      * is the runtime equivalent of setting the 'editable' config option at config time.
41501      * @param {Boolean} value True to allow the user to directly edit the field text
41502      */
41503     setEditable : function(value){
41504         if(value == this.editable){
41505             return;
41506         }
41507         this.editable = value;
41508         if(!value){
41509             this.el.dom.setAttribute('readOnly', true);
41510             this.el.on('mousedown', this.onTriggerClick,  this);
41511             this.el.addClass('x-combo-noedit');
41512         }else{
41513             this.el.dom.setAttribute('readOnly', false);
41514             this.el.un('mousedown', this.onTriggerClick,  this);
41515             this.el.removeClass('x-combo-noedit');
41516         }
41517     },
41518
41519     // private
41520     onBeforeLoad : function(){
41521         if(!this.hasFocus){
41522             return;
41523         }
41524         this.innerList.update(this.loadingText ?
41525                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41526         this.restrictHeight();
41527         this.selectedIndex = -1;
41528     },
41529
41530     // private
41531     onLoad : function(){
41532         if(!this.hasFocus){
41533             return;
41534         }
41535         if(this.store.getCount() > 0){
41536             this.expand();
41537             this.restrictHeight();
41538             if(this.lastQuery == this.allQuery){
41539                 if(this.editable){
41540                     this.el.dom.select();
41541                 }
41542                 if(!this.selectByValue(this.value, true)){
41543                     this.select(0, true);
41544                 }
41545             }else{
41546                 this.selectNext();
41547                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41548                     this.taTask.delay(this.typeAheadDelay);
41549                 }
41550             }
41551         }else{
41552             this.onEmptyResults();
41553         }
41554         //this.el.focus();
41555     },
41556     // private
41557     onLoadException : function()
41558     {
41559         this.collapse();
41560         Roo.log(this.store.reader.jsonData);
41561         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41562             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41563         }
41564         
41565         
41566     },
41567     // private
41568     onTypeAhead : function(){
41569         if(this.store.getCount() > 0){
41570             var r = this.store.getAt(0);
41571             var newValue = r.data[this.displayField];
41572             var len = newValue.length;
41573             var selStart = this.getRawValue().length;
41574             if(selStart != len){
41575                 this.setRawValue(newValue);
41576                 this.selectText(selStart, newValue.length);
41577             }
41578         }
41579     },
41580
41581     // private
41582     onSelect : function(record, index){
41583         if(this.fireEvent('beforeselect', this, record, index) !== false){
41584             this.setFromData(index > -1 ? record.data : false);
41585             this.collapse();
41586             this.fireEvent('select', this, record, index);
41587         }
41588     },
41589
41590     /**
41591      * Returns the currently selected field value or empty string if no value is set.
41592      * @return {String} value The selected value
41593      */
41594     getValue : function(){
41595         if(this.valueField){
41596             return typeof this.value != 'undefined' ? this.value : '';
41597         }
41598         return Roo.form.ComboBox.superclass.getValue.call(this);
41599     },
41600
41601     /**
41602      * Clears any text/value currently set in the field
41603      */
41604     clearValue : function(){
41605         if(this.hiddenField){
41606             this.hiddenField.value = '';
41607         }
41608         this.value = '';
41609         this.setRawValue('');
41610         this.lastSelectionText = '';
41611         
41612     },
41613
41614     /**
41615      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41616      * will be displayed in the field.  If the value does not match the data value of an existing item,
41617      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41618      * Otherwise the field will be blank (although the value will still be set).
41619      * @param {String} value The value to match
41620      */
41621     setValue : function(v){
41622         var text = v;
41623         if(this.valueField){
41624             var r = this.findRecord(this.valueField, v);
41625             if(r){
41626                 text = r.data[this.displayField];
41627             }else if(this.valueNotFoundText !== undefined){
41628                 text = this.valueNotFoundText;
41629             }
41630         }
41631         this.lastSelectionText = text;
41632         if(this.hiddenField){
41633             this.hiddenField.value = v;
41634         }
41635         Roo.form.ComboBox.superclass.setValue.call(this, text);
41636         this.value = v;
41637     },
41638     /**
41639      * @property {Object} the last set data for the element
41640      */
41641     
41642     lastData : false,
41643     /**
41644      * Sets the value of the field based on a object which is related to the record format for the store.
41645      * @param {Object} value the value to set as. or false on reset?
41646      */
41647     setFromData : function(o){
41648         var dv = ''; // display value
41649         var vv = ''; // value value..
41650         this.lastData = o;
41651         if (this.displayField) {
41652             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41653         } else {
41654             // this is an error condition!!!
41655             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41656         }
41657         
41658         if(this.valueField){
41659             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41660         }
41661         if(this.hiddenField){
41662             this.hiddenField.value = vv;
41663             
41664             this.lastSelectionText = dv;
41665             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41666             this.value = vv;
41667             return;
41668         }
41669         // no hidden field.. - we store the value in 'value', but still display
41670         // display field!!!!
41671         this.lastSelectionText = dv;
41672         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41673         this.value = vv;
41674         
41675         
41676     },
41677     // private
41678     reset : function(){
41679         // overridden so that last data is reset..
41680         this.setValue(this.resetValue);
41681         this.originalValue = this.getValue();
41682         this.clearInvalid();
41683         this.lastData = false;
41684         if (this.view) {
41685             this.view.clearSelections();
41686         }
41687     },
41688     // private
41689     findRecord : function(prop, value){
41690         var record;
41691         if(this.store.getCount() > 0){
41692             this.store.each(function(r){
41693                 if(r.data[prop] == value){
41694                     record = r;
41695                     return false;
41696                 }
41697                 return true;
41698             });
41699         }
41700         return record;
41701     },
41702     
41703     getName: function()
41704     {
41705         // returns hidden if it's set..
41706         if (!this.rendered) {return ''};
41707         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41708         
41709     },
41710     // private
41711     onViewMove : function(e, t){
41712         this.inKeyMode = false;
41713     },
41714
41715     // private
41716     onViewOver : function(e, t){
41717         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41718             return;
41719         }
41720         var item = this.view.findItemFromChild(t);
41721         if(item){
41722             var index = this.view.indexOf(item);
41723             this.select(index, false);
41724         }
41725     },
41726
41727     // private
41728     onViewClick : function(doFocus)
41729     {
41730         var index = this.view.getSelectedIndexes()[0];
41731         var r = this.store.getAt(index);
41732         if(r){
41733             this.onSelect(r, index);
41734         }
41735         if(doFocus !== false && !this.blockFocus){
41736             this.el.focus();
41737         }
41738     },
41739
41740     // private
41741     restrictHeight : function(){
41742         this.innerList.dom.style.height = '';
41743         var inner = this.innerList.dom;
41744         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41745         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41746         this.list.beginUpdate();
41747         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41748         this.list.alignTo(this.el, this.listAlign);
41749         this.list.endUpdate();
41750     },
41751
41752     // private
41753     onEmptyResults : function(){
41754         this.collapse();
41755     },
41756
41757     /**
41758      * Returns true if the dropdown list is expanded, else false.
41759      */
41760     isExpanded : function(){
41761         return this.list.isVisible();
41762     },
41763
41764     /**
41765      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41766      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41767      * @param {String} value The data value of the item to select
41768      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41769      * selected item if it is not currently in view (defaults to true)
41770      * @return {Boolean} True if the value matched an item in the list, else false
41771      */
41772     selectByValue : function(v, scrollIntoView){
41773         if(v !== undefined && v !== null){
41774             var r = this.findRecord(this.valueField || this.displayField, v);
41775             if(r){
41776                 this.select(this.store.indexOf(r), scrollIntoView);
41777                 return true;
41778             }
41779         }
41780         return false;
41781     },
41782
41783     /**
41784      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41785      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41786      * @param {Number} index The zero-based index of the list item to select
41787      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41788      * selected item if it is not currently in view (defaults to true)
41789      */
41790     select : function(index, scrollIntoView){
41791         this.selectedIndex = index;
41792         this.view.select(index);
41793         if(scrollIntoView !== false){
41794             var el = this.view.getNode(index);
41795             if(el){
41796                 this.innerList.scrollChildIntoView(el, false);
41797             }
41798         }
41799     },
41800
41801     // private
41802     selectNext : function(){
41803         var ct = this.store.getCount();
41804         if(ct > 0){
41805             if(this.selectedIndex == -1){
41806                 this.select(0);
41807             }else if(this.selectedIndex < ct-1){
41808                 this.select(this.selectedIndex+1);
41809             }
41810         }
41811     },
41812
41813     // private
41814     selectPrev : function(){
41815         var ct = this.store.getCount();
41816         if(ct > 0){
41817             if(this.selectedIndex == -1){
41818                 this.select(0);
41819             }else if(this.selectedIndex != 0){
41820                 this.select(this.selectedIndex-1);
41821             }
41822         }
41823     },
41824
41825     // private
41826     onKeyUp : function(e){
41827         if(this.editable !== false && !e.isSpecialKey()){
41828             this.lastKey = e.getKey();
41829             this.dqTask.delay(this.queryDelay);
41830         }
41831     },
41832
41833     // private
41834     validateBlur : function(){
41835         return !this.list || !this.list.isVisible();   
41836     },
41837
41838     // private
41839     initQuery : function(){
41840         this.doQuery(this.getRawValue());
41841     },
41842
41843     // private
41844     doForce : function(){
41845         if(this.el.dom.value.length > 0){
41846             this.el.dom.value =
41847                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41848              
41849         }
41850     },
41851
41852     /**
41853      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41854      * query allowing the query action to be canceled if needed.
41855      * @param {String} query The SQL query to execute
41856      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41857      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41858      * saved in the current store (defaults to false)
41859      */
41860     doQuery : function(q, forceAll){
41861         if(q === undefined || q === null){
41862             q = '';
41863         }
41864         var qe = {
41865             query: q,
41866             forceAll: forceAll,
41867             combo: this,
41868             cancel:false
41869         };
41870         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41871             return false;
41872         }
41873         q = qe.query;
41874         forceAll = qe.forceAll;
41875         if(forceAll === true || (q.length >= this.minChars)){
41876             if(this.lastQuery != q || this.alwaysQuery){
41877                 this.lastQuery = q;
41878                 if(this.mode == 'local'){
41879                     this.selectedIndex = -1;
41880                     if(forceAll){
41881                         this.store.clearFilter();
41882                     }else{
41883                         this.store.filter(this.displayField, q);
41884                     }
41885                     this.onLoad();
41886                 }else{
41887                     this.store.baseParams[this.queryParam] = q;
41888                     this.store.load({
41889                         params: this.getParams(q)
41890                     });
41891                     this.expand();
41892                 }
41893             }else{
41894                 this.selectedIndex = -1;
41895                 this.onLoad();   
41896             }
41897         }
41898     },
41899
41900     // private
41901     getParams : function(q){
41902         var p = {};
41903         //p[this.queryParam] = q;
41904         if(this.pageSize){
41905             p.start = 0;
41906             p.limit = this.pageSize;
41907         }
41908         return p;
41909     },
41910
41911     /**
41912      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
41913      */
41914     collapse : function(){
41915         if(!this.isExpanded()){
41916             return;
41917         }
41918         this.list.hide();
41919         Roo.get(document).un('mousedown', this.collapseIf, this);
41920         Roo.get(document).un('mousewheel', this.collapseIf, this);
41921         if (!this.editable) {
41922             Roo.get(document).un('keydown', this.listKeyPress, this);
41923         }
41924         this.fireEvent('collapse', this);
41925     },
41926
41927     // private
41928     collapseIf : function(e){
41929         if(!e.within(this.wrap) && !e.within(this.list)){
41930             this.collapse();
41931         }
41932     },
41933
41934     /**
41935      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
41936      */
41937     expand : function(){
41938         if(this.isExpanded() || !this.hasFocus){
41939             return;
41940         }
41941         this.list.alignTo(this.el, this.listAlign);
41942         this.list.show();
41943         Roo.get(document).on('mousedown', this.collapseIf, this);
41944         Roo.get(document).on('mousewheel', this.collapseIf, this);
41945         if (!this.editable) {
41946             Roo.get(document).on('keydown', this.listKeyPress, this);
41947         }
41948         
41949         this.fireEvent('expand', this);
41950     },
41951
41952     // private
41953     // Implements the default empty TriggerField.onTriggerClick function
41954     onTriggerClick : function(){
41955         if(this.disabled){
41956             return;
41957         }
41958         if(this.isExpanded()){
41959             this.collapse();
41960             if (!this.blockFocus) {
41961                 this.el.focus();
41962             }
41963             
41964         }else {
41965             this.hasFocus = true;
41966             if(this.triggerAction == 'all') {
41967                 this.doQuery(this.allQuery, true);
41968             } else {
41969                 this.doQuery(this.getRawValue());
41970             }
41971             if (!this.blockFocus) {
41972                 this.el.focus();
41973             }
41974         }
41975     },
41976     listKeyPress : function(e)
41977     {
41978         //Roo.log('listkeypress');
41979         // scroll to first matching element based on key pres..
41980         if (e.isSpecialKey()) {
41981             return false;
41982         }
41983         var k = String.fromCharCode(e.getKey()).toUpperCase();
41984         //Roo.log(k);
41985         var match  = false;
41986         var csel = this.view.getSelectedNodes();
41987         var cselitem = false;
41988         if (csel.length) {
41989             var ix = this.view.indexOf(csel[0]);
41990             cselitem  = this.store.getAt(ix);
41991             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
41992                 cselitem = false;
41993             }
41994             
41995         }
41996         
41997         this.store.each(function(v) { 
41998             if (cselitem) {
41999                 // start at existing selection.
42000                 if (cselitem.id == v.id) {
42001                     cselitem = false;
42002                 }
42003                 return;
42004             }
42005                 
42006             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42007                 match = this.store.indexOf(v);
42008                 return false;
42009             }
42010         }, this);
42011         
42012         if (match === false) {
42013             return true; // no more action?
42014         }
42015         // scroll to?
42016         this.view.select(match);
42017         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42018         sn.scrollIntoView(sn.dom.parentNode, false);
42019     }
42020
42021     /** 
42022     * @cfg {Boolean} grow 
42023     * @hide 
42024     */
42025     /** 
42026     * @cfg {Number} growMin 
42027     * @hide 
42028     */
42029     /** 
42030     * @cfg {Number} growMax 
42031     * @hide 
42032     */
42033     /**
42034      * @hide
42035      * @method autoSize
42036      */
42037 });/*
42038  * Copyright(c) 2010-2012, Roo J Solutions Limited
42039  *
42040  * Licence LGPL
42041  *
42042  */
42043
42044 /**
42045  * @class Roo.form.ComboBoxArray
42046  * @extends Roo.form.TextField
42047  * A facebook style adder... for lists of email / people / countries  etc...
42048  * pick multiple items from a combo box, and shows each one.
42049  *
42050  *  Fred [x]  Brian [x]  [Pick another |v]
42051  *
42052  *
42053  *  For this to work: it needs various extra information
42054  *    - normal combo problay has
42055  *      name, hiddenName
42056  *    + displayField, valueField
42057  *
42058  *    For our purpose...
42059  *
42060  *
42061  *   If we change from 'extends' to wrapping...
42062  *   
42063  *  
42064  *
42065  
42066  
42067  * @constructor
42068  * Create a new ComboBoxArray.
42069  * @param {Object} config Configuration options
42070  */
42071  
42072
42073 Roo.form.ComboBoxArray = function(config)
42074 {
42075     this.addEvents({
42076         /**
42077          * @event beforeremove
42078          * Fires before remove the value from the list
42079              * @param {Roo.form.ComboBoxArray} _self This combo box array
42080              * @param {Roo.form.ComboBoxArray.Item} item removed item
42081              */
42082         'beforeremove' : true,
42083         /**
42084          * @event remove
42085          * Fires when remove the value from the list
42086              * @param {Roo.form.ComboBoxArray} _self This combo box array
42087              * @param {Roo.form.ComboBoxArray.Item} item removed item
42088              */
42089         'remove' : true
42090         
42091         
42092     });
42093     
42094     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42095     
42096     this.items = new Roo.util.MixedCollection(false);
42097     
42098     // construct the child combo...
42099     
42100     
42101     
42102     
42103    
42104     
42105 }
42106
42107  
42108 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42109
42110     /**
42111      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42112      */
42113     
42114     lastData : false,
42115     
42116     // behavies liek a hiddne field
42117     inputType:      'hidden',
42118     /**
42119      * @cfg {Number} width The width of the box that displays the selected element
42120      */ 
42121     width:          300,
42122
42123     
42124     
42125     /**
42126      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42127      */
42128     name : false,
42129     /**
42130      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42131      */
42132     hiddenName : false,
42133     
42134     
42135     // private the array of items that are displayed..
42136     items  : false,
42137     // private - the hidden field el.
42138     hiddenEl : false,
42139     // private - the filed el..
42140     el : false,
42141     
42142     //validateValue : function() { return true; }, // all values are ok!
42143     //onAddClick: function() { },
42144     
42145     onRender : function(ct, position) 
42146     {
42147         
42148         // create the standard hidden element
42149         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42150         
42151         
42152         // give fake names to child combo;
42153         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42154         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
42155         
42156         this.combo = Roo.factory(this.combo, Roo.form);
42157         this.combo.onRender(ct, position);
42158         if (typeof(this.combo.width) != 'undefined') {
42159             this.combo.onResize(this.combo.width,0);
42160         }
42161         
42162         this.combo.initEvents();
42163         
42164         // assigned so form know we need to do this..
42165         this.store          = this.combo.store;
42166         this.valueField     = this.combo.valueField;
42167         this.displayField   = this.combo.displayField ;
42168         
42169         
42170         this.combo.wrap.addClass('x-cbarray-grp');
42171         
42172         var cbwrap = this.combo.wrap.createChild(
42173             {tag: 'div', cls: 'x-cbarray-cb'},
42174             this.combo.el.dom
42175         );
42176         
42177              
42178         this.hiddenEl = this.combo.wrap.createChild({
42179             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42180         });
42181         this.el = this.combo.wrap.createChild({
42182             tag: 'input',  type:'hidden' , name: this.name, value : ''
42183         });
42184          //   this.el.dom.removeAttribute("name");
42185         
42186         
42187         this.outerWrap = this.combo.wrap;
42188         this.wrap = cbwrap;
42189         
42190         this.outerWrap.setWidth(this.width);
42191         this.outerWrap.dom.removeChild(this.el.dom);
42192         
42193         this.wrap.dom.appendChild(this.el.dom);
42194         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42195         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42196         
42197         this.combo.trigger.setStyle('position','relative');
42198         this.combo.trigger.setStyle('left', '0px');
42199         this.combo.trigger.setStyle('top', '2px');
42200         
42201         this.combo.el.setStyle('vertical-align', 'text-bottom');
42202         
42203         //this.trigger.setStyle('vertical-align', 'top');
42204         
42205         // this should use the code from combo really... on('add' ....)
42206         if (this.adder) {
42207             
42208         
42209             this.adder = this.outerWrap.createChild(
42210                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42211             var _t = this;
42212             this.adder.on('click', function(e) {
42213                 _t.fireEvent('adderclick', this, e);
42214             }, _t);
42215         }
42216         //var _t = this;
42217         //this.adder.on('click', this.onAddClick, _t);
42218         
42219         
42220         this.combo.on('select', function(cb, rec, ix) {
42221             this.addItem(rec.data);
42222             
42223             cb.setValue('');
42224             cb.el.dom.value = '';
42225             //cb.lastData = rec.data;
42226             // add to list
42227             
42228         }, this);
42229         
42230         
42231     },
42232     
42233     
42234     getName: function()
42235     {
42236         // returns hidden if it's set..
42237         if (!this.rendered) {return ''};
42238         return  this.hiddenName ? this.hiddenName : this.name;
42239         
42240     },
42241     
42242     
42243     onResize: function(w, h){
42244         
42245         return;
42246         // not sure if this is needed..
42247         //this.combo.onResize(w,h);
42248         
42249         if(typeof w != 'number'){
42250             // we do not handle it!?!?
42251             return;
42252         }
42253         var tw = this.combo.trigger.getWidth();
42254         tw += this.addicon ? this.addicon.getWidth() : 0;
42255         tw += this.editicon ? this.editicon.getWidth() : 0;
42256         var x = w - tw;
42257         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42258             
42259         this.combo.trigger.setStyle('left', '0px');
42260         
42261         if(this.list && this.listWidth === undefined){
42262             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42263             this.list.setWidth(lw);
42264             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42265         }
42266         
42267     
42268         
42269     },
42270     
42271     addItem: function(rec)
42272     {
42273         var valueField = this.combo.valueField;
42274         var displayField = this.combo.displayField;
42275         if (this.items.indexOfKey(rec[valueField]) > -1) {
42276             //console.log("GOT " + rec.data.id);
42277             return;
42278         }
42279         
42280         var x = new Roo.form.ComboBoxArray.Item({
42281             //id : rec[this.idField],
42282             data : rec,
42283             displayField : displayField ,
42284             tipField : displayField ,
42285             cb : this
42286         });
42287         // use the 
42288         this.items.add(rec[valueField],x);
42289         // add it before the element..
42290         this.updateHiddenEl();
42291         x.render(this.outerWrap, this.wrap.dom);
42292         // add the image handler..
42293     },
42294     
42295     updateHiddenEl : function()
42296     {
42297         this.validate();
42298         if (!this.hiddenEl) {
42299             return;
42300         }
42301         var ar = [];
42302         var idField = this.combo.valueField;
42303         
42304         this.items.each(function(f) {
42305             ar.push(f.data[idField]);
42306            
42307         });
42308         this.hiddenEl.dom.value = ar.join(',');
42309         this.validate();
42310     },
42311     
42312     reset : function()
42313     {
42314         this.items.clear();
42315         
42316         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42317            el.remove();
42318         });
42319         
42320         this.el.dom.value = '';
42321         if (this.hiddenEl) {
42322             this.hiddenEl.dom.value = '';
42323         }
42324         
42325     },
42326     getValue: function()
42327     {
42328         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42329     },
42330     setValue: function(v) // not a valid action - must use addItems..
42331     {
42332          
42333         this.reset();
42334         
42335         
42336         
42337         if (this.store.isLocal && (typeof(v) == 'string')) {
42338             // then we can use the store to find the values..
42339             // comma seperated at present.. this needs to allow JSON based encoding..
42340             this.hiddenEl.value  = v;
42341             var v_ar = [];
42342             Roo.each(v.split(','), function(k) {
42343                 Roo.log("CHECK " + this.valueField + ',' + k);
42344                 var li = this.store.query(this.valueField, k);
42345                 if (!li.length) {
42346                     return;
42347                 }
42348                 var add = {};
42349                 add[this.valueField] = k;
42350                 add[this.displayField] = li.item(0).data[this.displayField];
42351                 
42352                 this.addItem(add);
42353             }, this) 
42354              
42355         }
42356         if (typeof(v) == 'object' ) {
42357             // then let's assume it's an array of objects..
42358             Roo.each(v, function(l) {
42359                 this.addItem(l);
42360             }, this);
42361              
42362         }
42363         
42364         
42365     },
42366     setFromData: function(v)
42367     {
42368         // this recieves an object, if setValues is called.
42369         this.reset();
42370         this.el.dom.value = v[this.displayField];
42371         this.hiddenEl.dom.value = v[this.valueField];
42372         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42373             return;
42374         }
42375         var kv = v[this.valueField];
42376         var dv = v[this.displayField];
42377         kv = typeof(kv) != 'string' ? '' : kv;
42378         dv = typeof(dv) != 'string' ? '' : dv;
42379         
42380         
42381         var keys = kv.split(',');
42382         var display = dv.split(',');
42383         for (var i = 0 ; i < keys.length; i++) {
42384             
42385             add = {};
42386             add[this.valueField] = keys[i];
42387             add[this.displayField] = display[i];
42388             this.addItem(add);
42389         }
42390       
42391         
42392     },
42393     
42394     /**
42395      * Validates the combox array value
42396      * @return {Boolean} True if the value is valid, else false
42397      */
42398     validate : function(){
42399         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42400             this.clearInvalid();
42401             return true;
42402         }
42403         return false;
42404     },
42405     
42406     validateValue : function(value){
42407         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42408         
42409     },
42410     
42411     /*@
42412      * overide
42413      * 
42414      */
42415     isDirty : function() {
42416         if(this.disabled) {
42417             return false;
42418         }
42419         
42420         try {
42421             var d = Roo.decode(String(this.originalValue));
42422         } catch (e) {
42423             return String(this.getValue()) !== String(this.originalValue);
42424         }
42425         
42426         var originalValue = [];
42427         
42428         for (var i = 0; i < d.length; i++){
42429             originalValue.push(d[i][this.valueField]);
42430         }
42431         
42432         return String(this.getValue()) !== String(originalValue.join(','));
42433         
42434     }
42435     
42436 });
42437
42438
42439
42440 /**
42441  * @class Roo.form.ComboBoxArray.Item
42442  * @extends Roo.BoxComponent
42443  * A selected item in the list
42444  *  Fred [x]  Brian [x]  [Pick another |v]
42445  * 
42446  * @constructor
42447  * Create a new item.
42448  * @param {Object} config Configuration options
42449  */
42450  
42451 Roo.form.ComboBoxArray.Item = function(config) {
42452     config.id = Roo.id();
42453     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42454 }
42455
42456 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42457     data : {},
42458     cb: false,
42459     displayField : false,
42460     tipField : false,
42461     
42462     
42463     defaultAutoCreate : {
42464         tag: 'div',
42465         cls: 'x-cbarray-item',
42466         cn : [ 
42467             { tag: 'div' },
42468             {
42469                 tag: 'img',
42470                 width:16,
42471                 height : 16,
42472                 src : Roo.BLANK_IMAGE_URL ,
42473                 align: 'center'
42474             }
42475         ]
42476         
42477     },
42478     
42479  
42480     onRender : function(ct, position)
42481     {
42482         Roo.form.Field.superclass.onRender.call(this, ct, position);
42483         
42484         if(!this.el){
42485             var cfg = this.getAutoCreate();
42486             this.el = ct.createChild(cfg, position);
42487         }
42488         
42489         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42490         
42491         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42492             this.cb.renderer(this.data) :
42493             String.format('{0}',this.data[this.displayField]);
42494         
42495             
42496         this.el.child('div').dom.setAttribute('qtip',
42497                         String.format('{0}',this.data[this.tipField])
42498         );
42499         
42500         this.el.child('img').on('click', this.remove, this);
42501         
42502     },
42503    
42504     remove : function()
42505     {
42506         if(this.cb.disabled){
42507             return;
42508         }
42509         
42510         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42511             this.cb.items.remove(this);
42512             this.el.child('img').un('click', this.remove, this);
42513             this.el.remove();
42514             this.cb.updateHiddenEl();
42515
42516             this.cb.fireEvent('remove', this.cb, this);
42517         }
42518         
42519     }
42520 });/*
42521  * Based on:
42522  * Ext JS Library 1.1.1
42523  * Copyright(c) 2006-2007, Ext JS, LLC.
42524  *
42525  * Originally Released Under LGPL - original licence link has changed is not relivant.
42526  *
42527  * Fork - LGPL
42528  * <script type="text/javascript">
42529  */
42530 /**
42531  * @class Roo.form.Checkbox
42532  * @extends Roo.form.Field
42533  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42534  * @constructor
42535  * Creates a new Checkbox
42536  * @param {Object} config Configuration options
42537  */
42538 Roo.form.Checkbox = function(config){
42539     Roo.form.Checkbox.superclass.constructor.call(this, config);
42540     this.addEvents({
42541         /**
42542          * @event check
42543          * Fires when the checkbox is checked or unchecked.
42544              * @param {Roo.form.Checkbox} this This checkbox
42545              * @param {Boolean} checked The new checked value
42546              */
42547         check : true
42548     });
42549 };
42550
42551 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42552     /**
42553      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42554      */
42555     focusClass : undefined,
42556     /**
42557      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42558      */
42559     fieldClass: "x-form-field",
42560     /**
42561      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42562      */
42563     checked: false,
42564     /**
42565      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42566      * {tag: "input", type: "checkbox", autocomplete: "off"})
42567      */
42568     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42569     /**
42570      * @cfg {String} boxLabel The text that appears beside the checkbox
42571      */
42572     boxLabel : "",
42573     /**
42574      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42575      */  
42576     inputValue : '1',
42577     /**
42578      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42579      */
42580      valueOff: '0', // value when not checked..
42581
42582     actionMode : 'viewEl', 
42583     //
42584     // private
42585     itemCls : 'x-menu-check-item x-form-item',
42586     groupClass : 'x-menu-group-item',
42587     inputType : 'hidden',
42588     
42589     
42590     inSetChecked: false, // check that we are not calling self...
42591     
42592     inputElement: false, // real input element?
42593     basedOn: false, // ????
42594     
42595     isFormField: true, // not sure where this is needed!!!!
42596
42597     onResize : function(){
42598         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42599         if(!this.boxLabel){
42600             this.el.alignTo(this.wrap, 'c-c');
42601         }
42602     },
42603
42604     initEvents : function(){
42605         Roo.form.Checkbox.superclass.initEvents.call(this);
42606         this.el.on("click", this.onClick,  this);
42607         this.el.on("change", this.onClick,  this);
42608     },
42609
42610
42611     getResizeEl : function(){
42612         return this.wrap;
42613     },
42614
42615     getPositionEl : function(){
42616         return this.wrap;
42617     },
42618
42619     // private
42620     onRender : function(ct, position){
42621         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42622         /*
42623         if(this.inputValue !== undefined){
42624             this.el.dom.value = this.inputValue;
42625         }
42626         */
42627         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42628         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42629         var viewEl = this.wrap.createChild({ 
42630             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42631         this.viewEl = viewEl;   
42632         this.wrap.on('click', this.onClick,  this); 
42633         
42634         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42635         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42636         
42637         
42638         
42639         if(this.boxLabel){
42640             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42641         //    viewEl.on('click', this.onClick,  this); 
42642         }
42643         //if(this.checked){
42644             this.setChecked(this.checked);
42645         //}else{
42646             //this.checked = this.el.dom;
42647         //}
42648
42649     },
42650
42651     // private
42652     initValue : Roo.emptyFn,
42653
42654     /**
42655      * Returns the checked state of the checkbox.
42656      * @return {Boolean} True if checked, else false
42657      */
42658     getValue : function(){
42659         if(this.el){
42660             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42661         }
42662         return this.valueOff;
42663         
42664     },
42665
42666         // private
42667     onClick : function(){ 
42668         if (this.disabled) {
42669             return;
42670         }
42671         this.setChecked(!this.checked);
42672
42673         //if(this.el.dom.checked != this.checked){
42674         //    this.setValue(this.el.dom.checked);
42675        // }
42676     },
42677
42678     /**
42679      * Sets the checked state of the checkbox.
42680      * On is always based on a string comparison between inputValue and the param.
42681      * @param {Boolean/String} value - the value to set 
42682      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42683      */
42684     setValue : function(v,suppressEvent){
42685         
42686         
42687         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42688         //if(this.el && this.el.dom){
42689         //    this.el.dom.checked = this.checked;
42690         //    this.el.dom.defaultChecked = this.checked;
42691         //}
42692         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42693         //this.fireEvent("check", this, this.checked);
42694     },
42695     // private..
42696     setChecked : function(state,suppressEvent)
42697     {
42698         if (this.inSetChecked) {
42699             this.checked = state;
42700             return;
42701         }
42702         
42703     
42704         if(this.wrap){
42705             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42706         }
42707         this.checked = state;
42708         if(suppressEvent !== true){
42709             this.fireEvent('check', this, state);
42710         }
42711         this.inSetChecked = true;
42712         this.el.dom.value = state ? this.inputValue : this.valueOff;
42713         this.inSetChecked = false;
42714         
42715     },
42716     // handle setting of hidden value by some other method!!?!?
42717     setFromHidden: function()
42718     {
42719         if(!this.el){
42720             return;
42721         }
42722         //console.log("SET FROM HIDDEN");
42723         //alert('setFrom hidden');
42724         this.setValue(this.el.dom.value);
42725     },
42726     
42727     onDestroy : function()
42728     {
42729         if(this.viewEl){
42730             Roo.get(this.viewEl).remove();
42731         }
42732          
42733         Roo.form.Checkbox.superclass.onDestroy.call(this);
42734     },
42735     
42736     setBoxLabel : function(str)
42737     {
42738         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42739     }
42740
42741 });/*
42742  * Based on:
42743  * Ext JS Library 1.1.1
42744  * Copyright(c) 2006-2007, Ext JS, LLC.
42745  *
42746  * Originally Released Under LGPL - original licence link has changed is not relivant.
42747  *
42748  * Fork - LGPL
42749  * <script type="text/javascript">
42750  */
42751  
42752 /**
42753  * @class Roo.form.Radio
42754  * @extends Roo.form.Checkbox
42755  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42756  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42757  * @constructor
42758  * Creates a new Radio
42759  * @param {Object} config Configuration options
42760  */
42761 Roo.form.Radio = function(){
42762     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42763 };
42764 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42765     inputType: 'radio',
42766
42767     /**
42768      * If this radio is part of a group, it will return the selected value
42769      * @return {String}
42770      */
42771     getGroupValue : function(){
42772         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42773     },
42774     
42775     
42776     onRender : function(ct, position){
42777         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42778         
42779         if(this.inputValue !== undefined){
42780             this.el.dom.value = this.inputValue;
42781         }
42782          
42783         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42784         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42785         //var viewEl = this.wrap.createChild({ 
42786         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42787         //this.viewEl = viewEl;   
42788         //this.wrap.on('click', this.onClick,  this); 
42789         
42790         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42791         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42792         
42793         
42794         
42795         if(this.boxLabel){
42796             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42797         //    viewEl.on('click', this.onClick,  this); 
42798         }
42799          if(this.checked){
42800             this.el.dom.checked =   'checked' ;
42801         }
42802          
42803     } 
42804     
42805     
42806 });//<script type="text/javascript">
42807
42808 /*
42809  * Based  Ext JS Library 1.1.1
42810  * Copyright(c) 2006-2007, Ext JS, LLC.
42811  * LGPL
42812  *
42813  */
42814  
42815 /**
42816  * @class Roo.HtmlEditorCore
42817  * @extends Roo.Component
42818  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42819  *
42820  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42821  */
42822
42823 Roo.HtmlEditorCore = function(config){
42824     
42825     
42826     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42827     
42828     
42829     this.addEvents({
42830         /**
42831          * @event initialize
42832          * Fires when the editor is fully initialized (including the iframe)
42833          * @param {Roo.HtmlEditorCore} this
42834          */
42835         initialize: true,
42836         /**
42837          * @event activate
42838          * Fires when the editor is first receives the focus. Any insertion must wait
42839          * until after this event.
42840          * @param {Roo.HtmlEditorCore} this
42841          */
42842         activate: true,
42843          /**
42844          * @event beforesync
42845          * Fires before the textarea is updated with content from the editor iframe. Return false
42846          * to cancel the sync.
42847          * @param {Roo.HtmlEditorCore} this
42848          * @param {String} html
42849          */
42850         beforesync: true,
42851          /**
42852          * @event beforepush
42853          * Fires before the iframe editor is updated with content from the textarea. Return false
42854          * to cancel the push.
42855          * @param {Roo.HtmlEditorCore} this
42856          * @param {String} html
42857          */
42858         beforepush: true,
42859          /**
42860          * @event sync
42861          * Fires when the textarea is updated with content from the editor iframe.
42862          * @param {Roo.HtmlEditorCore} this
42863          * @param {String} html
42864          */
42865         sync: true,
42866          /**
42867          * @event push
42868          * Fires when the iframe editor is updated with content from the textarea.
42869          * @param {Roo.HtmlEditorCore} this
42870          * @param {String} html
42871          */
42872         push: true,
42873         
42874         /**
42875          * @event editorevent
42876          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42877          * @param {Roo.HtmlEditorCore} this
42878          */
42879         editorevent: true
42880         
42881     });
42882     
42883     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
42884     
42885     // defaults : white / black...
42886     this.applyBlacklists();
42887     
42888     
42889     
42890 };
42891
42892
42893 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
42894
42895
42896      /**
42897      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
42898      */
42899     
42900     owner : false,
42901     
42902      /**
42903      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42904      *                        Roo.resizable.
42905      */
42906     resizable : false,
42907      /**
42908      * @cfg {Number} height (in pixels)
42909      */   
42910     height: 300,
42911    /**
42912      * @cfg {Number} width (in pixels)
42913      */   
42914     width: 500,
42915     
42916     /**
42917      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42918      * 
42919      */
42920     stylesheets: false,
42921     
42922     // id of frame..
42923     frameId: false,
42924     
42925     // private properties
42926     validationEvent : false,
42927     deferHeight: true,
42928     initialized : false,
42929     activated : false,
42930     sourceEditMode : false,
42931     onFocus : Roo.emptyFn,
42932     iframePad:3,
42933     hideMode:'offsets',
42934     
42935     clearUp: true,
42936     
42937     // blacklist + whitelisted elements..
42938     black: false,
42939     white: false,
42940      
42941     bodyCls : '',
42942
42943     /**
42944      * Protected method that will not generally be called directly. It
42945      * is called when the editor initializes the iframe with HTML contents. Override this method if you
42946      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
42947      */
42948     getDocMarkup : function(){
42949         // body styles..
42950         var st = '';
42951         
42952         // inherit styels from page...?? 
42953         if (this.stylesheets === false) {
42954             
42955             Roo.get(document.head).select('style').each(function(node) {
42956                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42957             });
42958             
42959             Roo.get(document.head).select('link').each(function(node) { 
42960                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42961             });
42962             
42963         } else if (!this.stylesheets.length) {
42964                 // simple..
42965                 st = '<style type="text/css">' +
42966                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42967                    '</style>';
42968         } else { 
42969             st = '<style type="text/css">' +
42970                     this.stylesheets +
42971                 '</style>';
42972         }
42973         
42974         st +=  '<style type="text/css">' +
42975             'IMG { cursor: pointer } ' +
42976         '</style>';
42977
42978         var cls = 'roo-htmleditor-body';
42979         
42980         if(this.bodyCls.length){
42981             cls += ' ' + this.bodyCls;
42982         }
42983         
42984         return '<html><head>' + st  +
42985             //<style type="text/css">' +
42986             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42987             //'</style>' +
42988             ' </head><body class="' +  cls + '"></body></html>';
42989     },
42990
42991     // private
42992     onRender : function(ct, position)
42993     {
42994         var _t = this;
42995         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
42996         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
42997         
42998         
42999         this.el.dom.style.border = '0 none';
43000         this.el.dom.setAttribute('tabIndex', -1);
43001         this.el.addClass('x-hidden hide');
43002         
43003         
43004         
43005         if(Roo.isIE){ // fix IE 1px bogus margin
43006             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43007         }
43008        
43009         
43010         this.frameId = Roo.id();
43011         
43012          
43013         
43014         var iframe = this.owner.wrap.createChild({
43015             tag: 'iframe',
43016             cls: 'form-control', // bootstrap..
43017             id: this.frameId,
43018             name: this.frameId,
43019             frameBorder : 'no',
43020             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43021         }, this.el
43022         );
43023         
43024         
43025         this.iframe = iframe.dom;
43026
43027          this.assignDocWin();
43028         
43029         this.doc.designMode = 'on';
43030        
43031         this.doc.open();
43032         this.doc.write(this.getDocMarkup());
43033         this.doc.close();
43034
43035         
43036         var task = { // must defer to wait for browser to be ready
43037             run : function(){
43038                 //console.log("run task?" + this.doc.readyState);
43039                 this.assignDocWin();
43040                 if(this.doc.body || this.doc.readyState == 'complete'){
43041                     try {
43042                         this.doc.designMode="on";
43043                     } catch (e) {
43044                         return;
43045                     }
43046                     Roo.TaskMgr.stop(task);
43047                     this.initEditor.defer(10, this);
43048                 }
43049             },
43050             interval : 10,
43051             duration: 10000,
43052             scope: this
43053         };
43054         Roo.TaskMgr.start(task);
43055
43056     },
43057
43058     // private
43059     onResize : function(w, h)
43060     {
43061          Roo.log('resize: ' +w + ',' + h );
43062         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43063         if(!this.iframe){
43064             return;
43065         }
43066         if(typeof w == 'number'){
43067             
43068             this.iframe.style.width = w + 'px';
43069         }
43070         if(typeof h == 'number'){
43071             
43072             this.iframe.style.height = h + 'px';
43073             if(this.doc){
43074                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43075             }
43076         }
43077         
43078     },
43079
43080     /**
43081      * Toggles the editor between standard and source edit mode.
43082      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43083      */
43084     toggleSourceEdit : function(sourceEditMode){
43085         
43086         this.sourceEditMode = sourceEditMode === true;
43087         
43088         if(this.sourceEditMode){
43089  
43090             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43091             
43092         }else{
43093             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43094             //this.iframe.className = '';
43095             this.deferFocus();
43096         }
43097         //this.setSize(this.owner.wrap.getSize());
43098         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43099     },
43100
43101     
43102   
43103
43104     /**
43105      * Protected method that will not generally be called directly. If you need/want
43106      * custom HTML cleanup, this is the method you should override.
43107      * @param {String} html The HTML to be cleaned
43108      * return {String} The cleaned HTML
43109      */
43110     cleanHtml : function(html){
43111         html = String(html);
43112         if(html.length > 5){
43113             if(Roo.isSafari){ // strip safari nonsense
43114                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43115             }
43116         }
43117         if(html == '&nbsp;'){
43118             html = '';
43119         }
43120         return html;
43121     },
43122
43123     /**
43124      * HTML Editor -> Textarea
43125      * Protected method that will not generally be called directly. Syncs the contents
43126      * of the editor iframe with the textarea.
43127      */
43128     syncValue : function(){
43129         if(this.initialized){
43130             var bd = (this.doc.body || this.doc.documentElement);
43131             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43132             var html = bd.innerHTML;
43133             if(Roo.isSafari){
43134                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43135                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43136                 if(m && m[1]){
43137                     html = '<div style="'+m[0]+'">' + html + '</div>';
43138                 }
43139             }
43140             html = this.cleanHtml(html);
43141             // fix up the special chars.. normaly like back quotes in word...
43142             // however we do not want to do this with chinese..
43143             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
43144                 var cc = b.charCodeAt();
43145                 if (
43146                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43147                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43148                     (cc >= 0xf900 && cc < 0xfb00 )
43149                 ) {
43150                         return b;
43151                 }
43152                 return "&#"+cc+";" 
43153             });
43154             if(this.owner.fireEvent('beforesync', this, html) !== false){
43155                 this.el.dom.value = html;
43156                 this.owner.fireEvent('sync', this, html);
43157             }
43158         }
43159     },
43160
43161     /**
43162      * Protected method that will not generally be called directly. Pushes the value of the textarea
43163      * into the iframe editor.
43164      */
43165     pushValue : function(){
43166         if(this.initialized){
43167             var v = this.el.dom.value.trim();
43168             
43169 //            if(v.length < 1){
43170 //                v = '&#160;';
43171 //            }
43172             
43173             if(this.owner.fireEvent('beforepush', this, v) !== false){
43174                 var d = (this.doc.body || this.doc.documentElement);
43175                 d.innerHTML = v;
43176                 this.cleanUpPaste();
43177                 this.el.dom.value = d.innerHTML;
43178                 this.owner.fireEvent('push', this, v);
43179             }
43180         }
43181     },
43182
43183     // private
43184     deferFocus : function(){
43185         this.focus.defer(10, this);
43186     },
43187
43188     // doc'ed in Field
43189     focus : function(){
43190         if(this.win && !this.sourceEditMode){
43191             this.win.focus();
43192         }else{
43193             this.el.focus();
43194         }
43195     },
43196     
43197     assignDocWin: function()
43198     {
43199         var iframe = this.iframe;
43200         
43201          if(Roo.isIE){
43202             this.doc = iframe.contentWindow.document;
43203             this.win = iframe.contentWindow;
43204         } else {
43205 //            if (!Roo.get(this.frameId)) {
43206 //                return;
43207 //            }
43208 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43209 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43210             
43211             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43212                 return;
43213             }
43214             
43215             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43216             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43217         }
43218     },
43219     
43220     // private
43221     initEditor : function(){
43222         //console.log("INIT EDITOR");
43223         this.assignDocWin();
43224         
43225         
43226         
43227         this.doc.designMode="on";
43228         this.doc.open();
43229         this.doc.write(this.getDocMarkup());
43230         this.doc.close();
43231         
43232         var dbody = (this.doc.body || this.doc.documentElement);
43233         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43234         // this copies styles from the containing element into thsi one..
43235         // not sure why we need all of this..
43236         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43237         
43238         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43239         //ss['background-attachment'] = 'fixed'; // w3c
43240         dbody.bgProperties = 'fixed'; // ie
43241         //Roo.DomHelper.applyStyles(dbody, ss);
43242         Roo.EventManager.on(this.doc, {
43243             //'mousedown': this.onEditorEvent,
43244             'mouseup': this.onEditorEvent,
43245             'dblclick': this.onEditorEvent,
43246             'click': this.onEditorEvent,
43247             'keyup': this.onEditorEvent,
43248             buffer:100,
43249             scope: this
43250         });
43251         if(Roo.isGecko){
43252             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43253         }
43254         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43255             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43256         }
43257         this.initialized = true;
43258
43259         this.owner.fireEvent('initialize', this);
43260         this.pushValue();
43261     },
43262
43263     // private
43264     onDestroy : function(){
43265         
43266         
43267         
43268         if(this.rendered){
43269             
43270             //for (var i =0; i < this.toolbars.length;i++) {
43271             //    // fixme - ask toolbars for heights?
43272             //    this.toolbars[i].onDestroy();
43273            // }
43274             
43275             //this.wrap.dom.innerHTML = '';
43276             //this.wrap.remove();
43277         }
43278     },
43279
43280     // private
43281     onFirstFocus : function(){
43282         
43283         this.assignDocWin();
43284         
43285         
43286         this.activated = true;
43287          
43288     
43289         if(Roo.isGecko){ // prevent silly gecko errors
43290             this.win.focus();
43291             var s = this.win.getSelection();
43292             if(!s.focusNode || s.focusNode.nodeType != 3){
43293                 var r = s.getRangeAt(0);
43294                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43295                 r.collapse(true);
43296                 this.deferFocus();
43297             }
43298             try{
43299                 this.execCmd('useCSS', true);
43300                 this.execCmd('styleWithCSS', false);
43301             }catch(e){}
43302         }
43303         this.owner.fireEvent('activate', this);
43304     },
43305
43306     // private
43307     adjustFont: function(btn){
43308         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43309         //if(Roo.isSafari){ // safari
43310         //    adjust *= 2;
43311        // }
43312         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43313         if(Roo.isSafari){ // safari
43314             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43315             v =  (v < 10) ? 10 : v;
43316             v =  (v > 48) ? 48 : v;
43317             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43318             
43319         }
43320         
43321         
43322         v = Math.max(1, v+adjust);
43323         
43324         this.execCmd('FontSize', v  );
43325     },
43326
43327     onEditorEvent : function(e)
43328     {
43329         this.owner.fireEvent('editorevent', this, e);
43330       //  this.updateToolbar();
43331         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43332     },
43333
43334     insertTag : function(tg)
43335     {
43336         // could be a bit smarter... -> wrap the current selected tRoo..
43337         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
43338             
43339             range = this.createRange(this.getSelection());
43340             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43341             wrappingNode.appendChild(range.extractContents());
43342             range.insertNode(wrappingNode);
43343
43344             return;
43345             
43346             
43347             
43348         }
43349         this.execCmd("formatblock",   tg);
43350         
43351     },
43352     
43353     insertText : function(txt)
43354     {
43355         
43356         
43357         var range = this.createRange();
43358         range.deleteContents();
43359                //alert(Sender.getAttribute('label'));
43360                
43361         range.insertNode(this.doc.createTextNode(txt));
43362     } ,
43363     
43364      
43365
43366     /**
43367      * Executes a Midas editor command on the editor document and performs necessary focus and
43368      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43369      * @param {String} cmd The Midas command
43370      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43371      */
43372     relayCmd : function(cmd, value){
43373         this.win.focus();
43374         this.execCmd(cmd, value);
43375         this.owner.fireEvent('editorevent', this);
43376         //this.updateToolbar();
43377         this.owner.deferFocus();
43378     },
43379
43380     /**
43381      * Executes a Midas editor command directly on the editor document.
43382      * For visual commands, you should use {@link #relayCmd} instead.
43383      * <b>This should only be called after the editor is initialized.</b>
43384      * @param {String} cmd The Midas command
43385      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43386      */
43387     execCmd : function(cmd, value){
43388         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43389         this.syncValue();
43390     },
43391  
43392  
43393    
43394     /**
43395      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43396      * to insert tRoo.
43397      * @param {String} text | dom node.. 
43398      */
43399     insertAtCursor : function(text)
43400     {
43401         
43402         if(!this.activated){
43403             return;
43404         }
43405         /*
43406         if(Roo.isIE){
43407             this.win.focus();
43408             var r = this.doc.selection.createRange();
43409             if(r){
43410                 r.collapse(true);
43411                 r.pasteHTML(text);
43412                 this.syncValue();
43413                 this.deferFocus();
43414             
43415             }
43416             return;
43417         }
43418         */
43419         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43420             this.win.focus();
43421             
43422             
43423             // from jquery ui (MIT licenced)
43424             var range, node;
43425             var win = this.win;
43426             
43427             if (win.getSelection && win.getSelection().getRangeAt) {
43428                 range = win.getSelection().getRangeAt(0);
43429                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43430                 range.insertNode(node);
43431             } else if (win.document.selection && win.document.selection.createRange) {
43432                 // no firefox support
43433                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43434                 win.document.selection.createRange().pasteHTML(txt);
43435             } else {
43436                 // no firefox support
43437                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43438                 this.execCmd('InsertHTML', txt);
43439             } 
43440             
43441             this.syncValue();
43442             
43443             this.deferFocus();
43444         }
43445     },
43446  // private
43447     mozKeyPress : function(e){
43448         if(e.ctrlKey){
43449             var c = e.getCharCode(), cmd;
43450           
43451             if(c > 0){
43452                 c = String.fromCharCode(c).toLowerCase();
43453                 switch(c){
43454                     case 'b':
43455                         cmd = 'bold';
43456                         break;
43457                     case 'i':
43458                         cmd = 'italic';
43459                         break;
43460                     
43461                     case 'u':
43462                         cmd = 'underline';
43463                         break;
43464                     
43465                     case 'v':
43466                         this.cleanUpPaste.defer(100, this);
43467                         return;
43468                         
43469                 }
43470                 if(cmd){
43471                     this.win.focus();
43472                     this.execCmd(cmd);
43473                     this.deferFocus();
43474                     e.preventDefault();
43475                 }
43476                 
43477             }
43478         }
43479     },
43480
43481     // private
43482     fixKeys : function(){ // load time branching for fastest keydown performance
43483         if(Roo.isIE){
43484             return function(e){
43485                 var k = e.getKey(), r;
43486                 if(k == e.TAB){
43487                     e.stopEvent();
43488                     r = this.doc.selection.createRange();
43489                     if(r){
43490                         r.collapse(true);
43491                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43492                         this.deferFocus();
43493                     }
43494                     return;
43495                 }
43496                 
43497                 if(k == e.ENTER){
43498                     r = this.doc.selection.createRange();
43499                     if(r){
43500                         var target = r.parentElement();
43501                         if(!target || target.tagName.toLowerCase() != 'li'){
43502                             e.stopEvent();
43503                             r.pasteHTML('<br />');
43504                             r.collapse(false);
43505                             r.select();
43506                         }
43507                     }
43508                 }
43509                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43510                     this.cleanUpPaste.defer(100, this);
43511                     return;
43512                 }
43513                 
43514                 
43515             };
43516         }else if(Roo.isOpera){
43517             return function(e){
43518                 var k = e.getKey();
43519                 if(k == e.TAB){
43520                     e.stopEvent();
43521                     this.win.focus();
43522                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43523                     this.deferFocus();
43524                 }
43525                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43526                     this.cleanUpPaste.defer(100, this);
43527                     return;
43528                 }
43529                 
43530             };
43531         }else if(Roo.isSafari){
43532             return function(e){
43533                 var k = e.getKey();
43534                 
43535                 if(k == e.TAB){
43536                     e.stopEvent();
43537                     this.execCmd('InsertText','\t');
43538                     this.deferFocus();
43539                     return;
43540                 }
43541                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43542                     this.cleanUpPaste.defer(100, this);
43543                     return;
43544                 }
43545                 
43546              };
43547         }
43548     }(),
43549     
43550     getAllAncestors: function()
43551     {
43552         var p = this.getSelectedNode();
43553         var a = [];
43554         if (!p) {
43555             a.push(p); // push blank onto stack..
43556             p = this.getParentElement();
43557         }
43558         
43559         
43560         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43561             a.push(p);
43562             p = p.parentNode;
43563         }
43564         a.push(this.doc.body);
43565         return a;
43566     },
43567     lastSel : false,
43568     lastSelNode : false,
43569     
43570     
43571     getSelection : function() 
43572     {
43573         this.assignDocWin();
43574         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43575     },
43576     
43577     getSelectedNode: function() 
43578     {
43579         // this may only work on Gecko!!!
43580         
43581         // should we cache this!!!!
43582         
43583         
43584         
43585          
43586         var range = this.createRange(this.getSelection()).cloneRange();
43587         
43588         if (Roo.isIE) {
43589             var parent = range.parentElement();
43590             while (true) {
43591                 var testRange = range.duplicate();
43592                 testRange.moveToElementText(parent);
43593                 if (testRange.inRange(range)) {
43594                     break;
43595                 }
43596                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43597                     break;
43598                 }
43599                 parent = parent.parentElement;
43600             }
43601             return parent;
43602         }
43603         
43604         // is ancestor a text element.
43605         var ac =  range.commonAncestorContainer;
43606         if (ac.nodeType == 3) {
43607             ac = ac.parentNode;
43608         }
43609         
43610         var ar = ac.childNodes;
43611          
43612         var nodes = [];
43613         var other_nodes = [];
43614         var has_other_nodes = false;
43615         for (var i=0;i<ar.length;i++) {
43616             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43617                 continue;
43618             }
43619             // fullly contained node.
43620             
43621             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43622                 nodes.push(ar[i]);
43623                 continue;
43624             }
43625             
43626             // probably selected..
43627             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43628                 other_nodes.push(ar[i]);
43629                 continue;
43630             }
43631             // outer..
43632             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43633                 continue;
43634             }
43635             
43636             
43637             has_other_nodes = true;
43638         }
43639         if (!nodes.length && other_nodes.length) {
43640             nodes= other_nodes;
43641         }
43642         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43643             return false;
43644         }
43645         
43646         return nodes[0];
43647     },
43648     createRange: function(sel)
43649     {
43650         // this has strange effects when using with 
43651         // top toolbar - not sure if it's a great idea.
43652         //this.editor.contentWindow.focus();
43653         if (typeof sel != "undefined") {
43654             try {
43655                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43656             } catch(e) {
43657                 return this.doc.createRange();
43658             }
43659         } else {
43660             return this.doc.createRange();
43661         }
43662     },
43663     getParentElement: function()
43664     {
43665         
43666         this.assignDocWin();
43667         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43668         
43669         var range = this.createRange(sel);
43670          
43671         try {
43672             var p = range.commonAncestorContainer;
43673             while (p.nodeType == 3) { // text node
43674                 p = p.parentNode;
43675             }
43676             return p;
43677         } catch (e) {
43678             return null;
43679         }
43680     
43681     },
43682     /***
43683      *
43684      * Range intersection.. the hard stuff...
43685      *  '-1' = before
43686      *  '0' = hits..
43687      *  '1' = after.
43688      *         [ -- selected range --- ]
43689      *   [fail]                        [fail]
43690      *
43691      *    basically..
43692      *      if end is before start or  hits it. fail.
43693      *      if start is after end or hits it fail.
43694      *
43695      *   if either hits (but other is outside. - then it's not 
43696      *   
43697      *    
43698      **/
43699     
43700     
43701     // @see http://www.thismuchiknow.co.uk/?p=64.
43702     rangeIntersectsNode : function(range, node)
43703     {
43704         var nodeRange = node.ownerDocument.createRange();
43705         try {
43706             nodeRange.selectNode(node);
43707         } catch (e) {
43708             nodeRange.selectNodeContents(node);
43709         }
43710     
43711         var rangeStartRange = range.cloneRange();
43712         rangeStartRange.collapse(true);
43713     
43714         var rangeEndRange = range.cloneRange();
43715         rangeEndRange.collapse(false);
43716     
43717         var nodeStartRange = nodeRange.cloneRange();
43718         nodeStartRange.collapse(true);
43719     
43720         var nodeEndRange = nodeRange.cloneRange();
43721         nodeEndRange.collapse(false);
43722     
43723         return rangeStartRange.compareBoundaryPoints(
43724                  Range.START_TO_START, nodeEndRange) == -1 &&
43725                rangeEndRange.compareBoundaryPoints(
43726                  Range.START_TO_START, nodeStartRange) == 1;
43727         
43728          
43729     },
43730     rangeCompareNode : function(range, node)
43731     {
43732         var nodeRange = node.ownerDocument.createRange();
43733         try {
43734             nodeRange.selectNode(node);
43735         } catch (e) {
43736             nodeRange.selectNodeContents(node);
43737         }
43738         
43739         
43740         range.collapse(true);
43741     
43742         nodeRange.collapse(true);
43743      
43744         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43745         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43746          
43747         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43748         
43749         var nodeIsBefore   =  ss == 1;
43750         var nodeIsAfter    = ee == -1;
43751         
43752         if (nodeIsBefore && nodeIsAfter) {
43753             return 0; // outer
43754         }
43755         if (!nodeIsBefore && nodeIsAfter) {
43756             return 1; //right trailed.
43757         }
43758         
43759         if (nodeIsBefore && !nodeIsAfter) {
43760             return 2;  // left trailed.
43761         }
43762         // fully contined.
43763         return 3;
43764     },
43765
43766     // private? - in a new class?
43767     cleanUpPaste :  function()
43768     {
43769         // cleans up the whole document..
43770         Roo.log('cleanuppaste');
43771         
43772         this.cleanUpChildren(this.doc.body);
43773         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43774         if (clean != this.doc.body.innerHTML) {
43775             this.doc.body.innerHTML = clean;
43776         }
43777         
43778     },
43779     
43780     cleanWordChars : function(input) {// change the chars to hex code
43781         var he = Roo.HtmlEditorCore;
43782         
43783         var output = input;
43784         Roo.each(he.swapCodes, function(sw) { 
43785             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43786             
43787             output = output.replace(swapper, sw[1]);
43788         });
43789         
43790         return output;
43791     },
43792     
43793     
43794     cleanUpChildren : function (n)
43795     {
43796         if (!n.childNodes.length) {
43797             return;
43798         }
43799         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43800            this.cleanUpChild(n.childNodes[i]);
43801         }
43802     },
43803     
43804     
43805         
43806     
43807     cleanUpChild : function (node)
43808     {
43809         var ed = this;
43810         //console.log(node);
43811         if (node.nodeName == "#text") {
43812             // clean up silly Windows -- stuff?
43813             return; 
43814         }
43815         if (node.nodeName == "#comment") {
43816             node.parentNode.removeChild(node);
43817             // clean up silly Windows -- stuff?
43818             return; 
43819         }
43820         var lcname = node.tagName.toLowerCase();
43821         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43822         // whitelist of tags..
43823         
43824         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43825             // remove node.
43826             node.parentNode.removeChild(node);
43827             return;
43828             
43829         }
43830         
43831         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43832         
43833         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43834         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43835         
43836         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43837         //    remove_keep_children = true;
43838         //}
43839         
43840         if (remove_keep_children) {
43841             this.cleanUpChildren(node);
43842             // inserts everything just before this node...
43843             while (node.childNodes.length) {
43844                 var cn = node.childNodes[0];
43845                 node.removeChild(cn);
43846                 node.parentNode.insertBefore(cn, node);
43847             }
43848             node.parentNode.removeChild(node);
43849             return;
43850         }
43851         
43852         if (!node.attributes || !node.attributes.length) {
43853             this.cleanUpChildren(node);
43854             return;
43855         }
43856         
43857         function cleanAttr(n,v)
43858         {
43859             
43860             if (v.match(/^\./) || v.match(/^\//)) {
43861                 return;
43862             }
43863             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
43864                 return;
43865             }
43866             if (v.match(/^#/)) {
43867                 return;
43868             }
43869 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
43870             node.removeAttribute(n);
43871             
43872         }
43873         
43874         var cwhite = this.cwhite;
43875         var cblack = this.cblack;
43876             
43877         function cleanStyle(n,v)
43878         {
43879             if (v.match(/expression/)) { //XSS?? should we even bother..
43880                 node.removeAttribute(n);
43881                 return;
43882             }
43883             
43884             var parts = v.split(/;/);
43885             var clean = [];
43886             
43887             Roo.each(parts, function(p) {
43888                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
43889                 if (!p.length) {
43890                     return true;
43891                 }
43892                 var l = p.split(':').shift().replace(/\s+/g,'');
43893                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
43894                 
43895                 if ( cwhite.length && cblack.indexOf(l) > -1) {
43896 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43897                     //node.removeAttribute(n);
43898                     return true;
43899                 }
43900                 //Roo.log()
43901                 // only allow 'c whitelisted system attributes'
43902                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
43903 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43904                     //node.removeAttribute(n);
43905                     return true;
43906                 }
43907                 
43908                 
43909                  
43910                 
43911                 clean.push(p);
43912                 return true;
43913             });
43914             if (clean.length) { 
43915                 node.setAttribute(n, clean.join(';'));
43916             } else {
43917                 node.removeAttribute(n);
43918             }
43919             
43920         }
43921         
43922         
43923         for (var i = node.attributes.length-1; i > -1 ; i--) {
43924             var a = node.attributes[i];
43925             //console.log(a);
43926             
43927             if (a.name.toLowerCase().substr(0,2)=='on')  {
43928                 node.removeAttribute(a.name);
43929                 continue;
43930             }
43931             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
43932                 node.removeAttribute(a.name);
43933                 continue;
43934             }
43935             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
43936                 cleanAttr(a.name,a.value); // fixme..
43937                 continue;
43938             }
43939             if (a.name == 'style') {
43940                 cleanStyle(a.name,a.value);
43941                 continue;
43942             }
43943             /// clean up MS crap..
43944             // tecnically this should be a list of valid class'es..
43945             
43946             
43947             if (a.name == 'class') {
43948                 if (a.value.match(/^Mso/)) {
43949                     node.className = '';
43950                 }
43951                 
43952                 if (a.value.match(/^body$/)) {
43953                     node.className = '';
43954                 }
43955                 continue;
43956             }
43957             
43958             // style cleanup!?
43959             // class cleanup?
43960             
43961         }
43962         
43963         
43964         this.cleanUpChildren(node);
43965         
43966         
43967     },
43968     
43969     /**
43970      * Clean up MS wordisms...
43971      */
43972     cleanWord : function(node)
43973     {
43974         
43975         
43976         if (!node) {
43977             this.cleanWord(this.doc.body);
43978             return;
43979         }
43980         if (node.nodeName == "#text") {
43981             // clean up silly Windows -- stuff?
43982             return; 
43983         }
43984         if (node.nodeName == "#comment") {
43985             node.parentNode.removeChild(node);
43986             // clean up silly Windows -- stuff?
43987             return; 
43988         }
43989         
43990         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
43991             node.parentNode.removeChild(node);
43992             return;
43993         }
43994         
43995         // remove - but keep children..
43996         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
43997             while (node.childNodes.length) {
43998                 var cn = node.childNodes[0];
43999                 node.removeChild(cn);
44000                 node.parentNode.insertBefore(cn, node);
44001             }
44002             node.parentNode.removeChild(node);
44003             this.iterateChildren(node, this.cleanWord);
44004             return;
44005         }
44006         // clean styles
44007         if (node.className.length) {
44008             
44009             var cn = node.className.split(/\W+/);
44010             var cna = [];
44011             Roo.each(cn, function(cls) {
44012                 if (cls.match(/Mso[a-zA-Z]+/)) {
44013                     return;
44014                 }
44015                 cna.push(cls);
44016             });
44017             node.className = cna.length ? cna.join(' ') : '';
44018             if (!cna.length) {
44019                 node.removeAttribute("class");
44020             }
44021         }
44022         
44023         if (node.hasAttribute("lang")) {
44024             node.removeAttribute("lang");
44025         }
44026         
44027         if (node.hasAttribute("style")) {
44028             
44029             var styles = node.getAttribute("style").split(";");
44030             var nstyle = [];
44031             Roo.each(styles, function(s) {
44032                 if (!s.match(/:/)) {
44033                     return;
44034                 }
44035                 var kv = s.split(":");
44036                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44037                     return;
44038                 }
44039                 // what ever is left... we allow.
44040                 nstyle.push(s);
44041             });
44042             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44043             if (!nstyle.length) {
44044                 node.removeAttribute('style');
44045             }
44046         }
44047         this.iterateChildren(node, this.cleanWord);
44048         
44049         
44050         
44051     },
44052     /**
44053      * iterateChildren of a Node, calling fn each time, using this as the scole..
44054      * @param {DomNode} node node to iterate children of.
44055      * @param {Function} fn method of this class to call on each item.
44056      */
44057     iterateChildren : function(node, fn)
44058     {
44059         if (!node.childNodes.length) {
44060                 return;
44061         }
44062         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44063            fn.call(this, node.childNodes[i])
44064         }
44065     },
44066     
44067     
44068     /**
44069      * cleanTableWidths.
44070      *
44071      * Quite often pasting from word etc.. results in tables with column and widths.
44072      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44073      *
44074      */
44075     cleanTableWidths : function(node)
44076     {
44077          
44078          
44079         if (!node) {
44080             this.cleanTableWidths(this.doc.body);
44081             return;
44082         }
44083         
44084         // ignore list...
44085         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44086             return; 
44087         }
44088         Roo.log(node.tagName);
44089         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44090             this.iterateChildren(node, this.cleanTableWidths);
44091             return;
44092         }
44093         if (node.hasAttribute('width')) {
44094             node.removeAttribute('width');
44095         }
44096         
44097          
44098         if (node.hasAttribute("style")) {
44099             // pretty basic...
44100             
44101             var styles = node.getAttribute("style").split(";");
44102             var nstyle = [];
44103             Roo.each(styles, function(s) {
44104                 if (!s.match(/:/)) {
44105                     return;
44106                 }
44107                 var kv = s.split(":");
44108                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44109                     return;
44110                 }
44111                 // what ever is left... we allow.
44112                 nstyle.push(s);
44113             });
44114             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44115             if (!nstyle.length) {
44116                 node.removeAttribute('style');
44117             }
44118         }
44119         
44120         this.iterateChildren(node, this.cleanTableWidths);
44121         
44122         
44123     },
44124     
44125     
44126     
44127     
44128     domToHTML : function(currentElement, depth, nopadtext) {
44129         
44130         depth = depth || 0;
44131         nopadtext = nopadtext || false;
44132     
44133         if (!currentElement) {
44134             return this.domToHTML(this.doc.body);
44135         }
44136         
44137         //Roo.log(currentElement);
44138         var j;
44139         var allText = false;
44140         var nodeName = currentElement.nodeName;
44141         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44142         
44143         if  (nodeName == '#text') {
44144             
44145             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44146         }
44147         
44148         
44149         var ret = '';
44150         if (nodeName != 'BODY') {
44151              
44152             var i = 0;
44153             // Prints the node tagName, such as <A>, <IMG>, etc
44154             if (tagName) {
44155                 var attr = [];
44156                 for(i = 0; i < currentElement.attributes.length;i++) {
44157                     // quoting?
44158                     var aname = currentElement.attributes.item(i).name;
44159                     if (!currentElement.attributes.item(i).value.length) {
44160                         continue;
44161                     }
44162                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44163                 }
44164                 
44165                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44166             } 
44167             else {
44168                 
44169                 // eack
44170             }
44171         } else {
44172             tagName = false;
44173         }
44174         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44175             return ret;
44176         }
44177         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44178             nopadtext = true;
44179         }
44180         
44181         
44182         // Traverse the tree
44183         i = 0;
44184         var currentElementChild = currentElement.childNodes.item(i);
44185         var allText = true;
44186         var innerHTML  = '';
44187         lastnode = '';
44188         while (currentElementChild) {
44189             // Formatting code (indent the tree so it looks nice on the screen)
44190             var nopad = nopadtext;
44191             if (lastnode == 'SPAN') {
44192                 nopad  = true;
44193             }
44194             // text
44195             if  (currentElementChild.nodeName == '#text') {
44196                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44197                 toadd = nopadtext ? toadd : toadd.trim();
44198                 if (!nopad && toadd.length > 80) {
44199                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44200                 }
44201                 innerHTML  += toadd;
44202                 
44203                 i++;
44204                 currentElementChild = currentElement.childNodes.item(i);
44205                 lastNode = '';
44206                 continue;
44207             }
44208             allText = false;
44209             
44210             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44211                 
44212             // Recursively traverse the tree structure of the child node
44213             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44214             lastnode = currentElementChild.nodeName;
44215             i++;
44216             currentElementChild=currentElement.childNodes.item(i);
44217         }
44218         
44219         ret += innerHTML;
44220         
44221         if (!allText) {
44222                 // The remaining code is mostly for formatting the tree
44223             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44224         }
44225         
44226         
44227         if (tagName) {
44228             ret+= "</"+tagName+">";
44229         }
44230         return ret;
44231         
44232     },
44233         
44234     applyBlacklists : function()
44235     {
44236         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44237         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44238         
44239         this.white = [];
44240         this.black = [];
44241         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44242             if (b.indexOf(tag) > -1) {
44243                 return;
44244             }
44245             this.white.push(tag);
44246             
44247         }, this);
44248         
44249         Roo.each(w, function(tag) {
44250             if (b.indexOf(tag) > -1) {
44251                 return;
44252             }
44253             if (this.white.indexOf(tag) > -1) {
44254                 return;
44255             }
44256             this.white.push(tag);
44257             
44258         }, this);
44259         
44260         
44261         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44262             if (w.indexOf(tag) > -1) {
44263                 return;
44264             }
44265             this.black.push(tag);
44266             
44267         }, this);
44268         
44269         Roo.each(b, function(tag) {
44270             if (w.indexOf(tag) > -1) {
44271                 return;
44272             }
44273             if (this.black.indexOf(tag) > -1) {
44274                 return;
44275             }
44276             this.black.push(tag);
44277             
44278         }, this);
44279         
44280         
44281         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44282         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44283         
44284         this.cwhite = [];
44285         this.cblack = [];
44286         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44287             if (b.indexOf(tag) > -1) {
44288                 return;
44289             }
44290             this.cwhite.push(tag);
44291             
44292         }, this);
44293         
44294         Roo.each(w, function(tag) {
44295             if (b.indexOf(tag) > -1) {
44296                 return;
44297             }
44298             if (this.cwhite.indexOf(tag) > -1) {
44299                 return;
44300             }
44301             this.cwhite.push(tag);
44302             
44303         }, this);
44304         
44305         
44306         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44307             if (w.indexOf(tag) > -1) {
44308                 return;
44309             }
44310             this.cblack.push(tag);
44311             
44312         }, this);
44313         
44314         Roo.each(b, function(tag) {
44315             if (w.indexOf(tag) > -1) {
44316                 return;
44317             }
44318             if (this.cblack.indexOf(tag) > -1) {
44319                 return;
44320             }
44321             this.cblack.push(tag);
44322             
44323         }, this);
44324     },
44325     
44326     setStylesheets : function(stylesheets)
44327     {
44328         if(typeof(stylesheets) == 'string'){
44329             Roo.get(this.iframe.contentDocument.head).createChild({
44330                 tag : 'link',
44331                 rel : 'stylesheet',
44332                 type : 'text/css',
44333                 href : stylesheets
44334             });
44335             
44336             return;
44337         }
44338         var _this = this;
44339      
44340         Roo.each(stylesheets, function(s) {
44341             if(!s.length){
44342                 return;
44343             }
44344             
44345             Roo.get(_this.iframe.contentDocument.head).createChild({
44346                 tag : 'link',
44347                 rel : 'stylesheet',
44348                 type : 'text/css',
44349                 href : s
44350             });
44351         });
44352
44353         
44354     },
44355     
44356     removeStylesheets : function()
44357     {
44358         var _this = this;
44359         
44360         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44361             s.remove();
44362         });
44363     },
44364     
44365     setStyle : function(style)
44366     {
44367         Roo.get(this.iframe.contentDocument.head).createChild({
44368             tag : 'style',
44369             type : 'text/css',
44370             html : style
44371         });
44372
44373         return;
44374     }
44375     
44376     // hide stuff that is not compatible
44377     /**
44378      * @event blur
44379      * @hide
44380      */
44381     /**
44382      * @event change
44383      * @hide
44384      */
44385     /**
44386      * @event focus
44387      * @hide
44388      */
44389     /**
44390      * @event specialkey
44391      * @hide
44392      */
44393     /**
44394      * @cfg {String} fieldClass @hide
44395      */
44396     /**
44397      * @cfg {String} focusClass @hide
44398      */
44399     /**
44400      * @cfg {String} autoCreate @hide
44401      */
44402     /**
44403      * @cfg {String} inputType @hide
44404      */
44405     /**
44406      * @cfg {String} invalidClass @hide
44407      */
44408     /**
44409      * @cfg {String} invalidText @hide
44410      */
44411     /**
44412      * @cfg {String} msgFx @hide
44413      */
44414     /**
44415      * @cfg {String} validateOnBlur @hide
44416      */
44417 });
44418
44419 Roo.HtmlEditorCore.white = [
44420         'area', 'br', 'img', 'input', 'hr', 'wbr',
44421         
44422        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44423        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44424        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44425        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44426        'table',   'ul',         'xmp', 
44427        
44428        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44429       'thead',   'tr', 
44430      
44431       'dir', 'menu', 'ol', 'ul', 'dl',
44432        
44433       'embed',  'object'
44434 ];
44435
44436
44437 Roo.HtmlEditorCore.black = [
44438     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44439         'applet', // 
44440         'base',   'basefont', 'bgsound', 'blink',  'body', 
44441         'frame',  'frameset', 'head',    'html',   'ilayer', 
44442         'iframe', 'layer',  'link',     'meta',    'object',   
44443         'script', 'style' ,'title',  'xml' // clean later..
44444 ];
44445 Roo.HtmlEditorCore.clean = [
44446     'script', 'style', 'title', 'xml'
44447 ];
44448 Roo.HtmlEditorCore.remove = [
44449     'font'
44450 ];
44451 // attributes..
44452
44453 Roo.HtmlEditorCore.ablack = [
44454     'on'
44455 ];
44456     
44457 Roo.HtmlEditorCore.aclean = [ 
44458     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44459 ];
44460
44461 // protocols..
44462 Roo.HtmlEditorCore.pwhite= [
44463         'http',  'https',  'mailto'
44464 ];
44465
44466 // white listed style attributes.
44467 Roo.HtmlEditorCore.cwhite= [
44468       //  'text-align', /// default is to allow most things..
44469       
44470          
44471 //        'font-size'//??
44472 ];
44473
44474 // black listed style attributes.
44475 Roo.HtmlEditorCore.cblack= [
44476       //  'font-size' -- this can be set by the project 
44477 ];
44478
44479
44480 Roo.HtmlEditorCore.swapCodes   =[ 
44481     [    8211, "--" ], 
44482     [    8212, "--" ], 
44483     [    8216,  "'" ],  
44484     [    8217, "'" ],  
44485     [    8220, '"' ],  
44486     [    8221, '"' ],  
44487     [    8226, "*" ],  
44488     [    8230, "..." ]
44489 ]; 
44490
44491     //<script type="text/javascript">
44492
44493 /*
44494  * Ext JS Library 1.1.1
44495  * Copyright(c) 2006-2007, Ext JS, LLC.
44496  * Licence LGPL
44497  * 
44498  */
44499  
44500  
44501 Roo.form.HtmlEditor = function(config){
44502     
44503     
44504     
44505     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44506     
44507     if (!this.toolbars) {
44508         this.toolbars = [];
44509     }
44510     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44511     
44512     
44513 };
44514
44515 /**
44516  * @class Roo.form.HtmlEditor
44517  * @extends Roo.form.Field
44518  * Provides a lightweight HTML Editor component.
44519  *
44520  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44521  * 
44522  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44523  * supported by this editor.</b><br/><br/>
44524  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44525  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44526  */
44527 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44528     /**
44529      * @cfg {Boolean} clearUp
44530      */
44531     clearUp : true,
44532       /**
44533      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44534      */
44535     toolbars : false,
44536    
44537      /**
44538      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44539      *                        Roo.resizable.
44540      */
44541     resizable : false,
44542      /**
44543      * @cfg {Number} height (in pixels)
44544      */   
44545     height: 300,
44546    /**
44547      * @cfg {Number} width (in pixels)
44548      */   
44549     width: 500,
44550     
44551     /**
44552      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44553      * 
44554      */
44555     stylesheets: false,
44556     
44557     
44558      /**
44559      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44560      * 
44561      */
44562     cblack: false,
44563     /**
44564      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44565      * 
44566      */
44567     cwhite: false,
44568     
44569      /**
44570      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44571      * 
44572      */
44573     black: false,
44574     /**
44575      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44576      * 
44577      */
44578     white: false,
44579     
44580     // id of frame..
44581     frameId: false,
44582     
44583     // private properties
44584     validationEvent : false,
44585     deferHeight: true,
44586     initialized : false,
44587     activated : false,
44588     
44589     onFocus : Roo.emptyFn,
44590     iframePad:3,
44591     hideMode:'offsets',
44592     
44593     actionMode : 'container', // defaults to hiding it...
44594     
44595     defaultAutoCreate : { // modified by initCompnoent..
44596         tag: "textarea",
44597         style:"width:500px;height:300px;",
44598         autocomplete: "new-password"
44599     },
44600
44601     // private
44602     initComponent : function(){
44603         this.addEvents({
44604             /**
44605              * @event initialize
44606              * Fires when the editor is fully initialized (including the iframe)
44607              * @param {HtmlEditor} this
44608              */
44609             initialize: true,
44610             /**
44611              * @event activate
44612              * Fires when the editor is first receives the focus. Any insertion must wait
44613              * until after this event.
44614              * @param {HtmlEditor} this
44615              */
44616             activate: true,
44617              /**
44618              * @event beforesync
44619              * Fires before the textarea is updated with content from the editor iframe. Return false
44620              * to cancel the sync.
44621              * @param {HtmlEditor} this
44622              * @param {String} html
44623              */
44624             beforesync: true,
44625              /**
44626              * @event beforepush
44627              * Fires before the iframe editor is updated with content from the textarea. Return false
44628              * to cancel the push.
44629              * @param {HtmlEditor} this
44630              * @param {String} html
44631              */
44632             beforepush: true,
44633              /**
44634              * @event sync
44635              * Fires when the textarea is updated with content from the editor iframe.
44636              * @param {HtmlEditor} this
44637              * @param {String} html
44638              */
44639             sync: true,
44640              /**
44641              * @event push
44642              * Fires when the iframe editor is updated with content from the textarea.
44643              * @param {HtmlEditor} this
44644              * @param {String} html
44645              */
44646             push: true,
44647              /**
44648              * @event editmodechange
44649              * Fires when the editor switches edit modes
44650              * @param {HtmlEditor} this
44651              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44652              */
44653             editmodechange: true,
44654             /**
44655              * @event editorevent
44656              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44657              * @param {HtmlEditor} this
44658              */
44659             editorevent: true,
44660             /**
44661              * @event firstfocus
44662              * Fires when on first focus - needed by toolbars..
44663              * @param {HtmlEditor} this
44664              */
44665             firstfocus: true,
44666             /**
44667              * @event autosave
44668              * Auto save the htmlEditor value as a file into Events
44669              * @param {HtmlEditor} this
44670              */
44671             autosave: true,
44672             /**
44673              * @event savedpreview
44674              * preview the saved version of htmlEditor
44675              * @param {HtmlEditor} this
44676              */
44677             savedpreview: true,
44678             
44679             /**
44680             * @event stylesheetsclick
44681             * Fires when press the Sytlesheets button
44682             * @param {Roo.HtmlEditorCore} this
44683             */
44684             stylesheetsclick: true
44685         });
44686         this.defaultAutoCreate =  {
44687             tag: "textarea",
44688             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44689             autocomplete: "new-password"
44690         };
44691     },
44692
44693     /**
44694      * Protected method that will not generally be called directly. It
44695      * is called when the editor creates its toolbar. Override this method if you need to
44696      * add custom toolbar buttons.
44697      * @param {HtmlEditor} editor
44698      */
44699     createToolbar : function(editor){
44700         Roo.log("create toolbars");
44701         if (!editor.toolbars || !editor.toolbars.length) {
44702             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44703         }
44704         
44705         for (var i =0 ; i < editor.toolbars.length;i++) {
44706             editor.toolbars[i] = Roo.factory(
44707                     typeof(editor.toolbars[i]) == 'string' ?
44708                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44709                 Roo.form.HtmlEditor);
44710             editor.toolbars[i].init(editor);
44711         }
44712          
44713         
44714     },
44715
44716      
44717     // private
44718     onRender : function(ct, position)
44719     {
44720         var _t = this;
44721         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44722         
44723         this.wrap = this.el.wrap({
44724             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44725         });
44726         
44727         this.editorcore.onRender(ct, position);
44728          
44729         if (this.resizable) {
44730             this.resizeEl = new Roo.Resizable(this.wrap, {
44731                 pinned : true,
44732                 wrap: true,
44733                 dynamic : true,
44734                 minHeight : this.height,
44735                 height: this.height,
44736                 handles : this.resizable,
44737                 width: this.width,
44738                 listeners : {
44739                     resize : function(r, w, h) {
44740                         _t.onResize(w,h); // -something
44741                     }
44742                 }
44743             });
44744             
44745         }
44746         this.createToolbar(this);
44747        
44748         
44749         if(!this.width){
44750             this.setSize(this.wrap.getSize());
44751         }
44752         if (this.resizeEl) {
44753             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44754             // should trigger onReize..
44755         }
44756         
44757         this.keyNav = new Roo.KeyNav(this.el, {
44758             
44759             "tab" : function(e){
44760                 e.preventDefault();
44761                 
44762                 var value = this.getValue();
44763                 
44764                 var start = this.el.dom.selectionStart;
44765                 var end = this.el.dom.selectionEnd;
44766                 
44767                 if(!e.shiftKey){
44768                     
44769                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44770                     this.el.dom.setSelectionRange(end + 1, end + 1);
44771                     return;
44772                 }
44773                 
44774                 var f = value.substring(0, start).split("\t");
44775                 
44776                 if(f.pop().length != 0){
44777                     return;
44778                 }
44779                 
44780                 this.setValue(f.join("\t") + value.substring(end));
44781                 this.el.dom.setSelectionRange(start - 1, start - 1);
44782                 
44783             },
44784             
44785             "home" : function(e){
44786                 e.preventDefault();
44787                 
44788                 var curr = this.el.dom.selectionStart;
44789                 var lines = this.getValue().split("\n");
44790                 
44791                 if(!lines.length){
44792                     return;
44793                 }
44794                 
44795                 if(e.ctrlKey){
44796                     this.el.dom.setSelectionRange(0, 0);
44797                     return;
44798                 }
44799                 
44800                 var pos = 0;
44801                 
44802                 for (var i = 0; i < lines.length;i++) {
44803                     pos += lines[i].length;
44804                     
44805                     if(i != 0){
44806                         pos += 1;
44807                     }
44808                     
44809                     if(pos < curr){
44810                         continue;
44811                     }
44812                     
44813                     pos -= lines[i].length;
44814                     
44815                     break;
44816                 }
44817                 
44818                 if(!e.shiftKey){
44819                     this.el.dom.setSelectionRange(pos, pos);
44820                     return;
44821                 }
44822                 
44823                 this.el.dom.selectionStart = pos;
44824                 this.el.dom.selectionEnd = curr;
44825             },
44826             
44827             "end" : function(e){
44828                 e.preventDefault();
44829                 
44830                 var curr = this.el.dom.selectionStart;
44831                 var lines = this.getValue().split("\n");
44832                 
44833                 if(!lines.length){
44834                     return;
44835                 }
44836                 
44837                 if(e.ctrlKey){
44838                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
44839                     return;
44840                 }
44841                 
44842                 var pos = 0;
44843                 
44844                 for (var i = 0; i < lines.length;i++) {
44845                     
44846                     pos += lines[i].length;
44847                     
44848                     if(i != 0){
44849                         pos += 1;
44850                     }
44851                     
44852                     if(pos < curr){
44853                         continue;
44854                     }
44855                     
44856                     break;
44857                 }
44858                 
44859                 if(!e.shiftKey){
44860                     this.el.dom.setSelectionRange(pos, pos);
44861                     return;
44862                 }
44863                 
44864                 this.el.dom.selectionStart = curr;
44865                 this.el.dom.selectionEnd = pos;
44866             },
44867
44868             scope : this,
44869
44870             doRelay : function(foo, bar, hname){
44871                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44872             },
44873
44874             forceKeyDown: true
44875         });
44876         
44877 //        if(this.autosave && this.w){
44878 //            this.autoSaveFn = setInterval(this.autosave, 1000);
44879 //        }
44880     },
44881
44882     // private
44883     onResize : function(w, h)
44884     {
44885         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
44886         var ew = false;
44887         var eh = false;
44888         
44889         if(this.el ){
44890             if(typeof w == 'number'){
44891                 var aw = w - this.wrap.getFrameWidth('lr');
44892                 this.el.setWidth(this.adjustWidth('textarea', aw));
44893                 ew = aw;
44894             }
44895             if(typeof h == 'number'){
44896                 var tbh = 0;
44897                 for (var i =0; i < this.toolbars.length;i++) {
44898                     // fixme - ask toolbars for heights?
44899                     tbh += this.toolbars[i].tb.el.getHeight();
44900                     if (this.toolbars[i].footer) {
44901                         tbh += this.toolbars[i].footer.el.getHeight();
44902                     }
44903                 }
44904                 
44905                 
44906                 
44907                 
44908                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
44909                 ah -= 5; // knock a few pixes off for look..
44910 //                Roo.log(ah);
44911                 this.el.setHeight(this.adjustWidth('textarea', ah));
44912                 var eh = ah;
44913             }
44914         }
44915         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
44916         this.editorcore.onResize(ew,eh);
44917         
44918     },
44919
44920     /**
44921      * Toggles the editor between standard and source edit mode.
44922      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44923      */
44924     toggleSourceEdit : function(sourceEditMode)
44925     {
44926         this.editorcore.toggleSourceEdit(sourceEditMode);
44927         
44928         if(this.editorcore.sourceEditMode){
44929             Roo.log('editor - showing textarea');
44930             
44931 //            Roo.log('in');
44932 //            Roo.log(this.syncValue());
44933             this.editorcore.syncValue();
44934             this.el.removeClass('x-hidden');
44935             this.el.dom.removeAttribute('tabIndex');
44936             this.el.focus();
44937             
44938             for (var i = 0; i < this.toolbars.length; i++) {
44939                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44940                     this.toolbars[i].tb.hide();
44941                     this.toolbars[i].footer.hide();
44942                 }
44943             }
44944             
44945         }else{
44946             Roo.log('editor - hiding textarea');
44947 //            Roo.log('out')
44948 //            Roo.log(this.pushValue()); 
44949             this.editorcore.pushValue();
44950             
44951             this.el.addClass('x-hidden');
44952             this.el.dom.setAttribute('tabIndex', -1);
44953             
44954             for (var i = 0; i < this.toolbars.length; i++) {
44955                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44956                     this.toolbars[i].tb.show();
44957                     this.toolbars[i].footer.show();
44958                 }
44959             }
44960             
44961             //this.deferFocus();
44962         }
44963         
44964         this.setSize(this.wrap.getSize());
44965         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
44966         
44967         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
44968     },
44969  
44970     // private (for BoxComponent)
44971     adjustSize : Roo.BoxComponent.prototype.adjustSize,
44972
44973     // private (for BoxComponent)
44974     getResizeEl : function(){
44975         return this.wrap;
44976     },
44977
44978     // private (for BoxComponent)
44979     getPositionEl : function(){
44980         return this.wrap;
44981     },
44982
44983     // private
44984     initEvents : function(){
44985         this.originalValue = this.getValue();
44986     },
44987
44988     /**
44989      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44990      * @method
44991      */
44992     markInvalid : Roo.emptyFn,
44993     /**
44994      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44995      * @method
44996      */
44997     clearInvalid : Roo.emptyFn,
44998
44999     setValue : function(v){
45000         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45001         this.editorcore.pushValue();
45002     },
45003
45004      
45005     // private
45006     deferFocus : function(){
45007         this.focus.defer(10, this);
45008     },
45009
45010     // doc'ed in Field
45011     focus : function(){
45012         this.editorcore.focus();
45013         
45014     },
45015       
45016
45017     // private
45018     onDestroy : function(){
45019         
45020         
45021         
45022         if(this.rendered){
45023             
45024             for (var i =0; i < this.toolbars.length;i++) {
45025                 // fixme - ask toolbars for heights?
45026                 this.toolbars[i].onDestroy();
45027             }
45028             
45029             this.wrap.dom.innerHTML = '';
45030             this.wrap.remove();
45031         }
45032     },
45033
45034     // private
45035     onFirstFocus : function(){
45036         //Roo.log("onFirstFocus");
45037         this.editorcore.onFirstFocus();
45038          for (var i =0; i < this.toolbars.length;i++) {
45039             this.toolbars[i].onFirstFocus();
45040         }
45041         
45042     },
45043     
45044     // private
45045     syncValue : function()
45046     {
45047         this.editorcore.syncValue();
45048     },
45049     
45050     pushValue : function()
45051     {
45052         this.editorcore.pushValue();
45053     },
45054     
45055     setStylesheets : function(stylesheets)
45056     {
45057         this.editorcore.setStylesheets(stylesheets);
45058     },
45059     
45060     removeStylesheets : function()
45061     {
45062         this.editorcore.removeStylesheets();
45063     }
45064      
45065     
45066     // hide stuff that is not compatible
45067     /**
45068      * @event blur
45069      * @hide
45070      */
45071     /**
45072      * @event change
45073      * @hide
45074      */
45075     /**
45076      * @event focus
45077      * @hide
45078      */
45079     /**
45080      * @event specialkey
45081      * @hide
45082      */
45083     /**
45084      * @cfg {String} fieldClass @hide
45085      */
45086     /**
45087      * @cfg {String} focusClass @hide
45088      */
45089     /**
45090      * @cfg {String} autoCreate @hide
45091      */
45092     /**
45093      * @cfg {String} inputType @hide
45094      */
45095     /**
45096      * @cfg {String} invalidClass @hide
45097      */
45098     /**
45099      * @cfg {String} invalidText @hide
45100      */
45101     /**
45102      * @cfg {String} msgFx @hide
45103      */
45104     /**
45105      * @cfg {String} validateOnBlur @hide
45106      */
45107 });
45108  
45109     // <script type="text/javascript">
45110 /*
45111  * Based on
45112  * Ext JS Library 1.1.1
45113  * Copyright(c) 2006-2007, Ext JS, LLC.
45114  *  
45115  
45116  */
45117
45118 /**
45119  * @class Roo.form.HtmlEditorToolbar1
45120  * Basic Toolbar
45121  * 
45122  * Usage:
45123  *
45124  new Roo.form.HtmlEditor({
45125     ....
45126     toolbars : [
45127         new Roo.form.HtmlEditorToolbar1({
45128             disable : { fonts: 1 , format: 1, ..., ... , ...],
45129             btns : [ .... ]
45130         })
45131     }
45132      
45133  * 
45134  * @cfg {Object} disable List of elements to disable..
45135  * @cfg {Array} btns List of additional buttons.
45136  * 
45137  * 
45138  * NEEDS Extra CSS? 
45139  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45140  */
45141  
45142 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45143 {
45144     
45145     Roo.apply(this, config);
45146     
45147     // default disabled, based on 'good practice'..
45148     this.disable = this.disable || {};
45149     Roo.applyIf(this.disable, {
45150         fontSize : true,
45151         colors : true,
45152         specialElements : true
45153     });
45154     
45155     
45156     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45157     // dont call parent... till later.
45158 }
45159
45160 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45161     
45162     tb: false,
45163     
45164     rendered: false,
45165     
45166     editor : false,
45167     editorcore : false,
45168     /**
45169      * @cfg {Object} disable  List of toolbar elements to disable
45170          
45171      */
45172     disable : false,
45173     
45174     
45175      /**
45176      * @cfg {String} createLinkText The default text for the create link prompt
45177      */
45178     createLinkText : 'Please enter the URL for the link:',
45179     /**
45180      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45181      */
45182     defaultLinkValue : 'http:/'+'/',
45183    
45184     
45185       /**
45186      * @cfg {Array} fontFamilies An array of available font families
45187      */
45188     fontFamilies : [
45189         'Arial',
45190         'Courier New',
45191         'Tahoma',
45192         'Times New Roman',
45193         'Verdana'
45194     ],
45195     
45196     specialChars : [
45197            "&#169;",
45198           "&#174;",     
45199           "&#8482;",    
45200           "&#163;" ,    
45201          // "&#8212;",    
45202           "&#8230;",    
45203           "&#247;" ,    
45204         //  "&#225;" ,     ?? a acute?
45205            "&#8364;"    , //Euro
45206        //   "&#8220;"    ,
45207         //  "&#8221;"    ,
45208         //  "&#8226;"    ,
45209           "&#176;"  //   , // degrees
45210
45211          // "&#233;"     , // e ecute
45212          // "&#250;"     , // u ecute?
45213     ],
45214     
45215     specialElements : [
45216         {
45217             text: "Insert Table",
45218             xtype: 'MenuItem',
45219             xns : Roo.Menu,
45220             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45221                 
45222         },
45223         {    
45224             text: "Insert Image",
45225             xtype: 'MenuItem',
45226             xns : Roo.Menu,
45227             ihtml : '<img src="about:blank"/>'
45228             
45229         }
45230         
45231          
45232     ],
45233     
45234     
45235     inputElements : [ 
45236             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45237             "input:submit", "input:button", "select", "textarea", "label" ],
45238     formats : [
45239         ["p"] ,  
45240         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45241         ["pre"],[ "code"], 
45242         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45243         ['div'],['span']
45244     ],
45245     
45246     cleanStyles : [
45247         "font-size"
45248     ],
45249      /**
45250      * @cfg {String} defaultFont default font to use.
45251      */
45252     defaultFont: 'tahoma',
45253    
45254     fontSelect : false,
45255     
45256     
45257     formatCombo : false,
45258     
45259     init : function(editor)
45260     {
45261         this.editor = editor;
45262         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45263         var editorcore = this.editorcore;
45264         
45265         var _t = this;
45266         
45267         var fid = editorcore.frameId;
45268         var etb = this;
45269         function btn(id, toggle, handler){
45270             var xid = fid + '-'+ id ;
45271             return {
45272                 id : xid,
45273                 cmd : id,
45274                 cls : 'x-btn-icon x-edit-'+id,
45275                 enableToggle:toggle !== false,
45276                 scope: _t, // was editor...
45277                 handler:handler||_t.relayBtnCmd,
45278                 clickEvent:'mousedown',
45279                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45280                 tabIndex:-1
45281             };
45282         }
45283         
45284         
45285         
45286         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45287         this.tb = tb;
45288          // stop form submits
45289         tb.el.on('click', function(e){
45290             e.preventDefault(); // what does this do?
45291         });
45292
45293         if(!this.disable.font) { // && !Roo.isSafari){
45294             /* why no safari for fonts 
45295             editor.fontSelect = tb.el.createChild({
45296                 tag:'select',
45297                 tabIndex: -1,
45298                 cls:'x-font-select',
45299                 html: this.createFontOptions()
45300             });
45301             
45302             editor.fontSelect.on('change', function(){
45303                 var font = editor.fontSelect.dom.value;
45304                 editor.relayCmd('fontname', font);
45305                 editor.deferFocus();
45306             }, editor);
45307             
45308             tb.add(
45309                 editor.fontSelect.dom,
45310                 '-'
45311             );
45312             */
45313             
45314         };
45315         if(!this.disable.formats){
45316             this.formatCombo = new Roo.form.ComboBox({
45317                 store: new Roo.data.SimpleStore({
45318                     id : 'tag',
45319                     fields: ['tag'],
45320                     data : this.formats // from states.js
45321                 }),
45322                 blockFocus : true,
45323                 name : '',
45324                 //autoCreate : {tag: "div",  size: "20"},
45325                 displayField:'tag',
45326                 typeAhead: false,
45327                 mode: 'local',
45328                 editable : false,
45329                 triggerAction: 'all',
45330                 emptyText:'Add tag',
45331                 selectOnFocus:true,
45332                 width:135,
45333                 listeners : {
45334                     'select': function(c, r, i) {
45335                         editorcore.insertTag(r.get('tag'));
45336                         editor.focus();
45337                     }
45338                 }
45339
45340             });
45341             tb.addField(this.formatCombo);
45342             
45343         }
45344         
45345         if(!this.disable.format){
45346             tb.add(
45347                 btn('bold'),
45348                 btn('italic'),
45349                 btn('underline'),
45350                 btn('strikethrough')
45351             );
45352         };
45353         if(!this.disable.fontSize){
45354             tb.add(
45355                 '-',
45356                 
45357                 
45358                 btn('increasefontsize', false, editorcore.adjustFont),
45359                 btn('decreasefontsize', false, editorcore.adjustFont)
45360             );
45361         };
45362         
45363         
45364         if(!this.disable.colors){
45365             tb.add(
45366                 '-', {
45367                     id:editorcore.frameId +'-forecolor',
45368                     cls:'x-btn-icon x-edit-forecolor',
45369                     clickEvent:'mousedown',
45370                     tooltip: this.buttonTips['forecolor'] || undefined,
45371                     tabIndex:-1,
45372                     menu : new Roo.menu.ColorMenu({
45373                         allowReselect: true,
45374                         focus: Roo.emptyFn,
45375                         value:'000000',
45376                         plain:true,
45377                         selectHandler: function(cp, color){
45378                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45379                             editor.deferFocus();
45380                         },
45381                         scope: editorcore,
45382                         clickEvent:'mousedown'
45383                     })
45384                 }, {
45385                     id:editorcore.frameId +'backcolor',
45386                     cls:'x-btn-icon x-edit-backcolor',
45387                     clickEvent:'mousedown',
45388                     tooltip: this.buttonTips['backcolor'] || undefined,
45389                     tabIndex:-1,
45390                     menu : new Roo.menu.ColorMenu({
45391                         focus: Roo.emptyFn,
45392                         value:'FFFFFF',
45393                         plain:true,
45394                         allowReselect: true,
45395                         selectHandler: function(cp, color){
45396                             if(Roo.isGecko){
45397                                 editorcore.execCmd('useCSS', false);
45398                                 editorcore.execCmd('hilitecolor', color);
45399                                 editorcore.execCmd('useCSS', true);
45400                                 editor.deferFocus();
45401                             }else{
45402                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45403                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45404                                 editor.deferFocus();
45405                             }
45406                         },
45407                         scope:editorcore,
45408                         clickEvent:'mousedown'
45409                     })
45410                 }
45411             );
45412         };
45413         // now add all the items...
45414         
45415
45416         if(!this.disable.alignments){
45417             tb.add(
45418                 '-',
45419                 btn('justifyleft'),
45420                 btn('justifycenter'),
45421                 btn('justifyright')
45422             );
45423         };
45424
45425         //if(!Roo.isSafari){
45426             if(!this.disable.links){
45427                 tb.add(
45428                     '-',
45429                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45430                 );
45431             };
45432
45433             if(!this.disable.lists){
45434                 tb.add(
45435                     '-',
45436                     btn('insertorderedlist'),
45437                     btn('insertunorderedlist')
45438                 );
45439             }
45440             if(!this.disable.sourceEdit){
45441                 tb.add(
45442                     '-',
45443                     btn('sourceedit', true, function(btn){
45444                         this.toggleSourceEdit(btn.pressed);
45445                     })
45446                 );
45447             }
45448         //}
45449         
45450         var smenu = { };
45451         // special menu.. - needs to be tidied up..
45452         if (!this.disable.special) {
45453             smenu = {
45454                 text: "&#169;",
45455                 cls: 'x-edit-none',
45456                 
45457                 menu : {
45458                     items : []
45459                 }
45460             };
45461             for (var i =0; i < this.specialChars.length; i++) {
45462                 smenu.menu.items.push({
45463                     
45464                     html: this.specialChars[i],
45465                     handler: function(a,b) {
45466                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45467                         //editor.insertAtCursor(a.html);
45468                         
45469                     },
45470                     tabIndex:-1
45471                 });
45472             }
45473             
45474             
45475             tb.add(smenu);
45476             
45477             
45478         }
45479         
45480         var cmenu = { };
45481         if (!this.disable.cleanStyles) {
45482             cmenu = {
45483                 cls: 'x-btn-icon x-btn-clear',
45484                 
45485                 menu : {
45486                     items : []
45487                 }
45488             };
45489             for (var i =0; i < this.cleanStyles.length; i++) {
45490                 cmenu.menu.items.push({
45491                     actiontype : this.cleanStyles[i],
45492                     html: 'Remove ' + this.cleanStyles[i],
45493                     handler: function(a,b) {
45494 //                        Roo.log(a);
45495 //                        Roo.log(b);
45496                         var c = Roo.get(editorcore.doc.body);
45497                         c.select('[style]').each(function(s) {
45498                             s.dom.style.removeProperty(a.actiontype);
45499                         });
45500                         editorcore.syncValue();
45501                     },
45502                     tabIndex:-1
45503                 });
45504             }
45505              cmenu.menu.items.push({
45506                 actiontype : 'tablewidths',
45507                 html: 'Remove Table Widths',
45508                 handler: function(a,b) {
45509                     editorcore.cleanTableWidths();
45510                     editorcore.syncValue();
45511                 },
45512                 tabIndex:-1
45513             });
45514             cmenu.menu.items.push({
45515                 actiontype : 'word',
45516                 html: 'Remove MS Word Formating',
45517                 handler: function(a,b) {
45518                     editorcore.cleanWord();
45519                     editorcore.syncValue();
45520                 },
45521                 tabIndex:-1
45522             });
45523             
45524             cmenu.menu.items.push({
45525                 actiontype : 'all',
45526                 html: 'Remove All Styles',
45527                 handler: function(a,b) {
45528                     
45529                     var c = Roo.get(editorcore.doc.body);
45530                     c.select('[style]').each(function(s) {
45531                         s.dom.removeAttribute('style');
45532                     });
45533                     editorcore.syncValue();
45534                 },
45535                 tabIndex:-1
45536             });
45537             
45538             cmenu.menu.items.push({
45539                 actiontype : 'all',
45540                 html: 'Remove All CSS Classes',
45541                 handler: function(a,b) {
45542                     
45543                     var c = Roo.get(editorcore.doc.body);
45544                     c.select('[class]').each(function(s) {
45545                         s.dom.className = '';
45546                     });
45547                     editorcore.syncValue();
45548                 },
45549                 tabIndex:-1
45550             });
45551             
45552              cmenu.menu.items.push({
45553                 actiontype : 'tidy',
45554                 html: 'Tidy HTML Source',
45555                 handler: function(a,b) {
45556                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45557                     editorcore.syncValue();
45558                 },
45559                 tabIndex:-1
45560             });
45561             
45562             
45563             tb.add(cmenu);
45564         }
45565          
45566         if (!this.disable.specialElements) {
45567             var semenu = {
45568                 text: "Other;",
45569                 cls: 'x-edit-none',
45570                 menu : {
45571                     items : []
45572                 }
45573             };
45574             for (var i =0; i < this.specialElements.length; i++) {
45575                 semenu.menu.items.push(
45576                     Roo.apply({ 
45577                         handler: function(a,b) {
45578                             editor.insertAtCursor(this.ihtml);
45579                         }
45580                     }, this.specialElements[i])
45581                 );
45582                     
45583             }
45584             
45585             tb.add(semenu);
45586             
45587             
45588         }
45589          
45590         
45591         if (this.btns) {
45592             for(var i =0; i< this.btns.length;i++) {
45593                 var b = Roo.factory(this.btns[i],Roo.form);
45594                 b.cls =  'x-edit-none';
45595                 
45596                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45597                     b.cls += ' x-init-enable';
45598                 }
45599                 
45600                 b.scope = editorcore;
45601                 tb.add(b);
45602             }
45603         
45604         }
45605         
45606         
45607         
45608         // disable everything...
45609         
45610         this.tb.items.each(function(item){
45611             
45612            if(
45613                 item.id != editorcore.frameId+ '-sourceedit' && 
45614                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45615             ){
45616                 
45617                 item.disable();
45618             }
45619         });
45620         this.rendered = true;
45621         
45622         // the all the btns;
45623         editor.on('editorevent', this.updateToolbar, this);
45624         // other toolbars need to implement this..
45625         //editor.on('editmodechange', this.updateToolbar, this);
45626     },
45627     
45628     
45629     relayBtnCmd : function(btn) {
45630         this.editorcore.relayCmd(btn.cmd);
45631     },
45632     // private used internally
45633     createLink : function(){
45634         Roo.log("create link?");
45635         var url = prompt(this.createLinkText, this.defaultLinkValue);
45636         if(url && url != 'http:/'+'/'){
45637             this.editorcore.relayCmd('createlink', url);
45638         }
45639     },
45640
45641     
45642     /**
45643      * Protected method that will not generally be called directly. It triggers
45644      * a toolbar update by reading the markup state of the current selection in the editor.
45645      */
45646     updateToolbar: function(){
45647
45648         if(!this.editorcore.activated){
45649             this.editor.onFirstFocus();
45650             return;
45651         }
45652
45653         var btns = this.tb.items.map, 
45654             doc = this.editorcore.doc,
45655             frameId = this.editorcore.frameId;
45656
45657         if(!this.disable.font && !Roo.isSafari){
45658             /*
45659             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45660             if(name != this.fontSelect.dom.value){
45661                 this.fontSelect.dom.value = name;
45662             }
45663             */
45664         }
45665         if(!this.disable.format){
45666             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45667             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45668             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45669             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45670         }
45671         if(!this.disable.alignments){
45672             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45673             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45674             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45675         }
45676         if(!Roo.isSafari && !this.disable.lists){
45677             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45678             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45679         }
45680         
45681         var ans = this.editorcore.getAllAncestors();
45682         if (this.formatCombo) {
45683             
45684             
45685             var store = this.formatCombo.store;
45686             this.formatCombo.setValue("");
45687             for (var i =0; i < ans.length;i++) {
45688                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45689                     // select it..
45690                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45691                     break;
45692                 }
45693             }
45694         }
45695         
45696         
45697         
45698         // hides menus... - so this cant be on a menu...
45699         Roo.menu.MenuMgr.hideAll();
45700
45701         //this.editorsyncValue();
45702     },
45703    
45704     
45705     createFontOptions : function(){
45706         var buf = [], fs = this.fontFamilies, ff, lc;
45707         
45708         
45709         
45710         for(var i = 0, len = fs.length; i< len; i++){
45711             ff = fs[i];
45712             lc = ff.toLowerCase();
45713             buf.push(
45714                 '<option value="',lc,'" style="font-family:',ff,';"',
45715                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45716                     ff,
45717                 '</option>'
45718             );
45719         }
45720         return buf.join('');
45721     },
45722     
45723     toggleSourceEdit : function(sourceEditMode){
45724         
45725         Roo.log("toolbar toogle");
45726         if(sourceEditMode === undefined){
45727             sourceEditMode = !this.sourceEditMode;
45728         }
45729         this.sourceEditMode = sourceEditMode === true;
45730         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45731         // just toggle the button?
45732         if(btn.pressed !== this.sourceEditMode){
45733             btn.toggle(this.sourceEditMode);
45734             return;
45735         }
45736         
45737         if(sourceEditMode){
45738             Roo.log("disabling buttons");
45739             this.tb.items.each(function(item){
45740                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45741                     item.disable();
45742                 }
45743             });
45744           
45745         }else{
45746             Roo.log("enabling buttons");
45747             if(this.editorcore.initialized){
45748                 this.tb.items.each(function(item){
45749                     item.enable();
45750                 });
45751             }
45752             
45753         }
45754         Roo.log("calling toggole on editor");
45755         // tell the editor that it's been pressed..
45756         this.editor.toggleSourceEdit(sourceEditMode);
45757        
45758     },
45759      /**
45760      * Object collection of toolbar tooltips for the buttons in the editor. The key
45761      * is the command id associated with that button and the value is a valid QuickTips object.
45762      * For example:
45763 <pre><code>
45764 {
45765     bold : {
45766         title: 'Bold (Ctrl+B)',
45767         text: 'Make the selected text bold.',
45768         cls: 'x-html-editor-tip'
45769     },
45770     italic : {
45771         title: 'Italic (Ctrl+I)',
45772         text: 'Make the selected text italic.',
45773         cls: 'x-html-editor-tip'
45774     },
45775     ...
45776 </code></pre>
45777     * @type Object
45778      */
45779     buttonTips : {
45780         bold : {
45781             title: 'Bold (Ctrl+B)',
45782             text: 'Make the selected text bold.',
45783             cls: 'x-html-editor-tip'
45784         },
45785         italic : {
45786             title: 'Italic (Ctrl+I)',
45787             text: 'Make the selected text italic.',
45788             cls: 'x-html-editor-tip'
45789         },
45790         underline : {
45791             title: 'Underline (Ctrl+U)',
45792             text: 'Underline the selected text.',
45793             cls: 'x-html-editor-tip'
45794         },
45795         strikethrough : {
45796             title: 'Strikethrough',
45797             text: 'Strikethrough the selected text.',
45798             cls: 'x-html-editor-tip'
45799         },
45800         increasefontsize : {
45801             title: 'Grow Text',
45802             text: 'Increase the font size.',
45803             cls: 'x-html-editor-tip'
45804         },
45805         decreasefontsize : {
45806             title: 'Shrink Text',
45807             text: 'Decrease the font size.',
45808             cls: 'x-html-editor-tip'
45809         },
45810         backcolor : {
45811             title: 'Text Highlight Color',
45812             text: 'Change the background color of the selected text.',
45813             cls: 'x-html-editor-tip'
45814         },
45815         forecolor : {
45816             title: 'Font Color',
45817             text: 'Change the color of the selected text.',
45818             cls: 'x-html-editor-tip'
45819         },
45820         justifyleft : {
45821             title: 'Align Text Left',
45822             text: 'Align text to the left.',
45823             cls: 'x-html-editor-tip'
45824         },
45825         justifycenter : {
45826             title: 'Center Text',
45827             text: 'Center text in the editor.',
45828             cls: 'x-html-editor-tip'
45829         },
45830         justifyright : {
45831             title: 'Align Text Right',
45832             text: 'Align text to the right.',
45833             cls: 'x-html-editor-tip'
45834         },
45835         insertunorderedlist : {
45836             title: 'Bullet List',
45837             text: 'Start a bulleted list.',
45838             cls: 'x-html-editor-tip'
45839         },
45840         insertorderedlist : {
45841             title: 'Numbered List',
45842             text: 'Start a numbered list.',
45843             cls: 'x-html-editor-tip'
45844         },
45845         createlink : {
45846             title: 'Hyperlink',
45847             text: 'Make the selected text a hyperlink.',
45848             cls: 'x-html-editor-tip'
45849         },
45850         sourceedit : {
45851             title: 'Source Edit',
45852             text: 'Switch to source editing mode.',
45853             cls: 'x-html-editor-tip'
45854         }
45855     },
45856     // private
45857     onDestroy : function(){
45858         if(this.rendered){
45859             
45860             this.tb.items.each(function(item){
45861                 if(item.menu){
45862                     item.menu.removeAll();
45863                     if(item.menu.el){
45864                         item.menu.el.destroy();
45865                     }
45866                 }
45867                 item.destroy();
45868             });
45869              
45870         }
45871     },
45872     onFirstFocus: function() {
45873         this.tb.items.each(function(item){
45874            item.enable();
45875         });
45876     }
45877 });
45878
45879
45880
45881
45882 // <script type="text/javascript">
45883 /*
45884  * Based on
45885  * Ext JS Library 1.1.1
45886  * Copyright(c) 2006-2007, Ext JS, LLC.
45887  *  
45888  
45889  */
45890
45891  
45892 /**
45893  * @class Roo.form.HtmlEditor.ToolbarContext
45894  * Context Toolbar
45895  * 
45896  * Usage:
45897  *
45898  new Roo.form.HtmlEditor({
45899     ....
45900     toolbars : [
45901         { xtype: 'ToolbarStandard', styles : {} }
45902         { xtype: 'ToolbarContext', disable : {} }
45903     ]
45904 })
45905
45906      
45907  * 
45908  * @config : {Object} disable List of elements to disable.. (not done yet.)
45909  * @config : {Object} styles  Map of styles available.
45910  * 
45911  */
45912
45913 Roo.form.HtmlEditor.ToolbarContext = function(config)
45914 {
45915     
45916     Roo.apply(this, config);
45917     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45918     // dont call parent... till later.
45919     this.styles = this.styles || {};
45920 }
45921
45922  
45923
45924 Roo.form.HtmlEditor.ToolbarContext.types = {
45925     'IMG' : {
45926         width : {
45927             title: "Width",
45928             width: 40
45929         },
45930         height:  {
45931             title: "Height",
45932             width: 40
45933         },
45934         align: {
45935             title: "Align",
45936             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45937             width : 80
45938             
45939         },
45940         border: {
45941             title: "Border",
45942             width: 40
45943         },
45944         alt: {
45945             title: "Alt",
45946             width: 120
45947         },
45948         src : {
45949             title: "Src",
45950             width: 220
45951         }
45952         
45953     },
45954     'A' : {
45955         name : {
45956             title: "Name",
45957             width: 50
45958         },
45959         target:  {
45960             title: "Target",
45961             width: 120
45962         },
45963         href:  {
45964             title: "Href",
45965             width: 220
45966         } // border?
45967         
45968     },
45969     'TABLE' : {
45970         rows : {
45971             title: "Rows",
45972             width: 20
45973         },
45974         cols : {
45975             title: "Cols",
45976             width: 20
45977         },
45978         width : {
45979             title: "Width",
45980             width: 40
45981         },
45982         height : {
45983             title: "Height",
45984             width: 40
45985         },
45986         border : {
45987             title: "Border",
45988             width: 20
45989         }
45990     },
45991     'TD' : {
45992         width : {
45993             title: "Width",
45994             width: 40
45995         },
45996         height : {
45997             title: "Height",
45998             width: 40
45999         },   
46000         align: {
46001             title: "Align",
46002             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46003             width: 80
46004         },
46005         valign: {
46006             title: "Valign",
46007             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46008             width: 80
46009         },
46010         colspan: {
46011             title: "Colspan",
46012             width: 20
46013             
46014         },
46015          'font-family'  : {
46016             title : "Font",
46017             style : 'fontFamily',
46018             displayField: 'display',
46019             optname : 'font-family',
46020             width: 140
46021         }
46022     },
46023     'INPUT' : {
46024         name : {
46025             title: "name",
46026             width: 120
46027         },
46028         value : {
46029             title: "Value",
46030             width: 120
46031         },
46032         width : {
46033             title: "Width",
46034             width: 40
46035         }
46036     },
46037     'LABEL' : {
46038         'for' : {
46039             title: "For",
46040             width: 120
46041         }
46042     },
46043     'TEXTAREA' : {
46044           name : {
46045             title: "name",
46046             width: 120
46047         },
46048         rows : {
46049             title: "Rows",
46050             width: 20
46051         },
46052         cols : {
46053             title: "Cols",
46054             width: 20
46055         }
46056     },
46057     'SELECT' : {
46058         name : {
46059             title: "name",
46060             width: 120
46061         },
46062         selectoptions : {
46063             title: "Options",
46064             width: 200
46065         }
46066     },
46067     
46068     // should we really allow this??
46069     // should this just be 
46070     'BODY' : {
46071         title : {
46072             title: "Title",
46073             width: 200,
46074             disabled : true
46075         }
46076     },
46077     'SPAN' : {
46078         'font-family'  : {
46079             title : "Font",
46080             style : 'fontFamily',
46081             displayField: 'display',
46082             optname : 'font-family',
46083             width: 140
46084         }
46085     },
46086     'DIV' : {
46087         'font-family'  : {
46088             title : "Font",
46089             style : 'fontFamily',
46090             displayField: 'display',
46091             optname : 'font-family',
46092             width: 140
46093         }
46094     },
46095      'P' : {
46096         'font-family'  : {
46097             title : "Font",
46098             style : 'fontFamily',
46099             displayField: 'display',
46100             optname : 'font-family',
46101             width: 140
46102         }
46103     },
46104     
46105     '*' : {
46106         // empty..
46107     }
46108
46109 };
46110
46111 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46112 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46113
46114 Roo.form.HtmlEditor.ToolbarContext.options = {
46115         'font-family'  : [ 
46116                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46117                 [ 'Courier New', 'Courier New'],
46118                 [ 'Tahoma', 'Tahoma'],
46119                 [ 'Times New Roman,serif', 'Times'],
46120                 [ 'Verdana','Verdana' ]
46121         ]
46122 };
46123
46124 // fixme - these need to be configurable..
46125  
46126
46127 //Roo.form.HtmlEditor.ToolbarContext.types
46128
46129
46130 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46131     
46132     tb: false,
46133     
46134     rendered: false,
46135     
46136     editor : false,
46137     editorcore : false,
46138     /**
46139      * @cfg {Object} disable  List of toolbar elements to disable
46140          
46141      */
46142     disable : false,
46143     /**
46144      * @cfg {Object} styles List of styles 
46145      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46146      *
46147      * These must be defined in the page, so they get rendered correctly..
46148      * .headline { }
46149      * TD.underline { }
46150      * 
46151      */
46152     styles : false,
46153     
46154     options: false,
46155     
46156     toolbars : false,
46157     
46158     init : function(editor)
46159     {
46160         this.editor = editor;
46161         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46162         var editorcore = this.editorcore;
46163         
46164         var fid = editorcore.frameId;
46165         var etb = this;
46166         function btn(id, toggle, handler){
46167             var xid = fid + '-'+ id ;
46168             return {
46169                 id : xid,
46170                 cmd : id,
46171                 cls : 'x-btn-icon x-edit-'+id,
46172                 enableToggle:toggle !== false,
46173                 scope: editorcore, // was editor...
46174                 handler:handler||editorcore.relayBtnCmd,
46175                 clickEvent:'mousedown',
46176                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46177                 tabIndex:-1
46178             };
46179         }
46180         // create a new element.
46181         var wdiv = editor.wrap.createChild({
46182                 tag: 'div'
46183             }, editor.wrap.dom.firstChild.nextSibling, true);
46184         
46185         // can we do this more than once??
46186         
46187          // stop form submits
46188       
46189  
46190         // disable everything...
46191         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46192         this.toolbars = {};
46193            
46194         for (var i in  ty) {
46195           
46196             this.toolbars[i] = this.buildToolbar(ty[i],i);
46197         }
46198         this.tb = this.toolbars.BODY;
46199         this.tb.el.show();
46200         this.buildFooter();
46201         this.footer.show();
46202         editor.on('hide', function( ) { this.footer.hide() }, this);
46203         editor.on('show', function( ) { this.footer.show() }, this);
46204         
46205          
46206         this.rendered = true;
46207         
46208         // the all the btns;
46209         editor.on('editorevent', this.updateToolbar, this);
46210         // other toolbars need to implement this..
46211         //editor.on('editmodechange', this.updateToolbar, this);
46212     },
46213     
46214     
46215     
46216     /**
46217      * Protected method that will not generally be called directly. It triggers
46218      * a toolbar update by reading the markup state of the current selection in the editor.
46219      *
46220      * Note you can force an update by calling on('editorevent', scope, false)
46221      */
46222     updateToolbar: function(editor,ev,sel){
46223
46224         //Roo.log(ev);
46225         // capture mouse up - this is handy for selecting images..
46226         // perhaps should go somewhere else...
46227         if(!this.editorcore.activated){
46228              this.editor.onFirstFocus();
46229             return;
46230         }
46231         
46232         
46233         
46234         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46235         // selectNode - might want to handle IE?
46236         if (ev &&
46237             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46238             ev.target && ev.target.tagName == 'IMG') {
46239             // they have click on an image...
46240             // let's see if we can change the selection...
46241             sel = ev.target;
46242          
46243               var nodeRange = sel.ownerDocument.createRange();
46244             try {
46245                 nodeRange.selectNode(sel);
46246             } catch (e) {
46247                 nodeRange.selectNodeContents(sel);
46248             }
46249             //nodeRange.collapse(true);
46250             var s = this.editorcore.win.getSelection();
46251             s.removeAllRanges();
46252             s.addRange(nodeRange);
46253         }  
46254         
46255       
46256         var updateFooter = sel ? false : true;
46257         
46258         
46259         var ans = this.editorcore.getAllAncestors();
46260         
46261         // pick
46262         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46263         
46264         if (!sel) { 
46265             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46266             sel = sel ? sel : this.editorcore.doc.body;
46267             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46268             
46269         }
46270         // pick a menu that exists..
46271         var tn = sel.tagName.toUpperCase();
46272         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46273         
46274         tn = sel.tagName.toUpperCase();
46275         
46276         var lastSel = this.tb.selectedNode;
46277         
46278         this.tb.selectedNode = sel;
46279         
46280         // if current menu does not match..
46281         
46282         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46283                 
46284             this.tb.el.hide();
46285             ///console.log("show: " + tn);
46286             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46287             this.tb.el.show();
46288             // update name
46289             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46290             
46291             
46292             // update attributes
46293             if (this.tb.fields) {
46294                 this.tb.fields.each(function(e) {
46295                     if (e.stylename) {
46296                         e.setValue(sel.style[e.stylename]);
46297                         return;
46298                     } 
46299                    e.setValue(sel.getAttribute(e.attrname));
46300                 });
46301             }
46302             
46303             var hasStyles = false;
46304             for(var i in this.styles) {
46305                 hasStyles = true;
46306                 break;
46307             }
46308             
46309             // update styles
46310             if (hasStyles) { 
46311                 var st = this.tb.fields.item(0);
46312                 
46313                 st.store.removeAll();
46314                
46315                 
46316                 var cn = sel.className.split(/\s+/);
46317                 
46318                 var avs = [];
46319                 if (this.styles['*']) {
46320                     
46321                     Roo.each(this.styles['*'], function(v) {
46322                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46323                     });
46324                 }
46325                 if (this.styles[tn]) { 
46326                     Roo.each(this.styles[tn], function(v) {
46327                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46328                     });
46329                 }
46330                 
46331                 st.store.loadData(avs);
46332                 st.collapse();
46333                 st.setValue(cn);
46334             }
46335             // flag our selected Node.
46336             this.tb.selectedNode = sel;
46337            
46338            
46339             Roo.menu.MenuMgr.hideAll();
46340
46341         }
46342         
46343         if (!updateFooter) {
46344             //this.footDisp.dom.innerHTML = ''; 
46345             return;
46346         }
46347         // update the footer
46348         //
46349         var html = '';
46350         
46351         this.footerEls = ans.reverse();
46352         Roo.each(this.footerEls, function(a,i) {
46353             if (!a) { return; }
46354             html += html.length ? ' &gt; '  :  '';
46355             
46356             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46357             
46358         });
46359        
46360         // 
46361         var sz = this.footDisp.up('td').getSize();
46362         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46363         this.footDisp.dom.style.marginLeft = '5px';
46364         
46365         this.footDisp.dom.style.overflow = 'hidden';
46366         
46367         this.footDisp.dom.innerHTML = html;
46368             
46369         //this.editorsyncValue();
46370     },
46371      
46372     
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         // need to do this for all the toolbars..
46393         this.tb.items.each(function(item){
46394            item.enable();
46395         });
46396     },
46397     buildToolbar: function(tlist, nm)
46398     {
46399         var editor = this.editor;
46400         var editorcore = this.editorcore;
46401          // create a new element.
46402         var wdiv = editor.wrap.createChild({
46403                 tag: 'div'
46404             }, editor.wrap.dom.firstChild.nextSibling, true);
46405         
46406        
46407         var tb = new Roo.Toolbar(wdiv);
46408         // add the name..
46409         
46410         tb.add(nm+ ":&nbsp;");
46411         
46412         var styles = [];
46413         for(var i in this.styles) {
46414             styles.push(i);
46415         }
46416         
46417         // styles...
46418         if (styles && styles.length) {
46419             
46420             // this needs a multi-select checkbox...
46421             tb.addField( new Roo.form.ComboBox({
46422                 store: new Roo.data.SimpleStore({
46423                     id : 'val',
46424                     fields: ['val', 'selected'],
46425                     data : [] 
46426                 }),
46427                 name : '-roo-edit-className',
46428                 attrname : 'className',
46429                 displayField: 'val',
46430                 typeAhead: false,
46431                 mode: 'local',
46432                 editable : false,
46433                 triggerAction: 'all',
46434                 emptyText:'Select Style',
46435                 selectOnFocus:true,
46436                 width: 130,
46437                 listeners : {
46438                     'select': function(c, r, i) {
46439                         // initial support only for on class per el..
46440                         tb.selectedNode.className =  r ? r.get('val') : '';
46441                         editorcore.syncValue();
46442                     }
46443                 }
46444     
46445             }));
46446         }
46447         
46448         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46449         var tbops = tbc.options;
46450         
46451         for (var i in tlist) {
46452             
46453             var item = tlist[i];
46454             tb.add(item.title + ":&nbsp;");
46455             
46456             
46457             //optname == used so you can configure the options available..
46458             var opts = item.opts ? item.opts : false;
46459             if (item.optname) {
46460                 opts = tbops[item.optname];
46461            
46462             }
46463             
46464             if (opts) {
46465                 // opts == pulldown..
46466                 tb.addField( new Roo.form.ComboBox({
46467                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46468                         id : 'val',
46469                         fields: ['val', 'display'],
46470                         data : opts  
46471                     }),
46472                     name : '-roo-edit-' + i,
46473                     attrname : i,
46474                     stylename : item.style ? item.style : false,
46475                     displayField: item.displayField ? item.displayField : 'val',
46476                     valueField :  'val',
46477                     typeAhead: false,
46478                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46479                     editable : false,
46480                     triggerAction: 'all',
46481                     emptyText:'Select',
46482                     selectOnFocus:true,
46483                     width: item.width ? item.width  : 130,
46484                     listeners : {
46485                         'select': function(c, r, i) {
46486                             if (c.stylename) {
46487                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46488                                 return;
46489                             }
46490                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46491                         }
46492                     }
46493
46494                 }));
46495                 continue;
46496                     
46497                  
46498                 
46499                 tb.addField( new Roo.form.TextField({
46500                     name: i,
46501                     width: 100,
46502                     //allowBlank:false,
46503                     value: ''
46504                 }));
46505                 continue;
46506             }
46507             tb.addField( new Roo.form.TextField({
46508                 name: '-roo-edit-' + i,
46509                 attrname : i,
46510                 
46511                 width: item.width,
46512                 //allowBlank:true,
46513                 value: '',
46514                 listeners: {
46515                     'change' : function(f, nv, ov) {
46516                         tb.selectedNode.setAttribute(f.attrname, nv);
46517                         editorcore.syncValue();
46518                     }
46519                 }
46520             }));
46521              
46522         }
46523         
46524         var _this = this;
46525         
46526         if(nm == 'BODY'){
46527             tb.addSeparator();
46528         
46529             tb.addButton( {
46530                 text: 'Stylesheets',
46531
46532                 listeners : {
46533                     click : function ()
46534                     {
46535                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46536                     }
46537                 }
46538             });
46539         }
46540         
46541         tb.addFill();
46542         tb.addButton( {
46543             text: 'Remove Tag',
46544     
46545             listeners : {
46546                 click : function ()
46547                 {
46548                     // remove
46549                     // undo does not work.
46550                      
46551                     var sn = tb.selectedNode;
46552                     
46553                     var pn = sn.parentNode;
46554                     
46555                     var stn =  sn.childNodes[0];
46556                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46557                     while (sn.childNodes.length) {
46558                         var node = sn.childNodes[0];
46559                         sn.removeChild(node);
46560                         //Roo.log(node);
46561                         pn.insertBefore(node, sn);
46562                         
46563                     }
46564                     pn.removeChild(sn);
46565                     var range = editorcore.createRange();
46566         
46567                     range.setStart(stn,0);
46568                     range.setEnd(en,0); //????
46569                     //range.selectNode(sel);
46570                     
46571                     
46572                     var selection = editorcore.getSelection();
46573                     selection.removeAllRanges();
46574                     selection.addRange(range);
46575                     
46576                     
46577                     
46578                     //_this.updateToolbar(null, null, pn);
46579                     _this.updateToolbar(null, null, null);
46580                     _this.footDisp.dom.innerHTML = ''; 
46581                 }
46582             }
46583             
46584                     
46585                 
46586             
46587         });
46588         
46589         
46590         tb.el.on('click', function(e){
46591             e.preventDefault(); // what does this do?
46592         });
46593         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46594         tb.el.hide();
46595         tb.name = nm;
46596         // dont need to disable them... as they will get hidden
46597         return tb;
46598          
46599         
46600     },
46601     buildFooter : function()
46602     {
46603         
46604         var fel = this.editor.wrap.createChild();
46605         this.footer = new Roo.Toolbar(fel);
46606         // toolbar has scrolly on left / right?
46607         var footDisp= new Roo.Toolbar.Fill();
46608         var _t = this;
46609         this.footer.add(
46610             {
46611                 text : '&lt;',
46612                 xtype: 'Button',
46613                 handler : function() {
46614                     _t.footDisp.scrollTo('left',0,true)
46615                 }
46616             }
46617         );
46618         this.footer.add( footDisp );
46619         this.footer.add( 
46620             {
46621                 text : '&gt;',
46622                 xtype: 'Button',
46623                 handler : function() {
46624                     // no animation..
46625                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46626                 }
46627             }
46628         );
46629         var fel = Roo.get(footDisp.el);
46630         fel.addClass('x-editor-context');
46631         this.footDispWrap = fel; 
46632         this.footDispWrap.overflow  = 'hidden';
46633         
46634         this.footDisp = fel.createChild();
46635         this.footDispWrap.on('click', this.onContextClick, this)
46636         
46637         
46638     },
46639     onContextClick : function (ev,dom)
46640     {
46641         ev.preventDefault();
46642         var  cn = dom.className;
46643         //Roo.log(cn);
46644         if (!cn.match(/x-ed-loc-/)) {
46645             return;
46646         }
46647         var n = cn.split('-').pop();
46648         var ans = this.footerEls;
46649         var sel = ans[n];
46650         
46651          // pick
46652         var range = this.editorcore.createRange();
46653         
46654         range.selectNodeContents(sel);
46655         //range.selectNode(sel);
46656         
46657         
46658         var selection = this.editorcore.getSelection();
46659         selection.removeAllRanges();
46660         selection.addRange(range);
46661         
46662         
46663         
46664         this.updateToolbar(null, null, sel);
46665         
46666         
46667     }
46668     
46669     
46670     
46671     
46672     
46673 });
46674
46675
46676
46677
46678
46679 /*
46680  * Based on:
46681  * Ext JS Library 1.1.1
46682  * Copyright(c) 2006-2007, Ext JS, LLC.
46683  *
46684  * Originally Released Under LGPL - original licence link has changed is not relivant.
46685  *
46686  * Fork - LGPL
46687  * <script type="text/javascript">
46688  */
46689  
46690 /**
46691  * @class Roo.form.BasicForm
46692  * @extends Roo.util.Observable
46693  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46694  * @constructor
46695  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46696  * @param {Object} config Configuration options
46697  */
46698 Roo.form.BasicForm = function(el, config){
46699     this.allItems = [];
46700     this.childForms = [];
46701     Roo.apply(this, config);
46702     /*
46703      * The Roo.form.Field items in this form.
46704      * @type MixedCollection
46705      */
46706      
46707      
46708     this.items = new Roo.util.MixedCollection(false, function(o){
46709         return o.id || (o.id = Roo.id());
46710     });
46711     this.addEvents({
46712         /**
46713          * @event beforeaction
46714          * Fires before any action is performed. Return false to cancel the action.
46715          * @param {Form} this
46716          * @param {Action} action The action to be performed
46717          */
46718         beforeaction: true,
46719         /**
46720          * @event actionfailed
46721          * Fires when an action fails.
46722          * @param {Form} this
46723          * @param {Action} action The action that failed
46724          */
46725         actionfailed : true,
46726         /**
46727          * @event actioncomplete
46728          * Fires when an action is completed.
46729          * @param {Form} this
46730          * @param {Action} action The action that completed
46731          */
46732         actioncomplete : true
46733     });
46734     if(el){
46735         this.initEl(el);
46736     }
46737     Roo.form.BasicForm.superclass.constructor.call(this);
46738 };
46739
46740 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46741     /**
46742      * @cfg {String} method
46743      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46744      */
46745     /**
46746      * @cfg {DataReader} reader
46747      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46748      * This is optional as there is built-in support for processing JSON.
46749      */
46750     /**
46751      * @cfg {DataReader} errorReader
46752      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46753      * This is completely optional as there is built-in support for processing JSON.
46754      */
46755     /**
46756      * @cfg {String} url
46757      * The URL to use for form actions if one isn't supplied in the action options.
46758      */
46759     /**
46760      * @cfg {Boolean} fileUpload
46761      * Set to true if this form is a file upload.
46762      */
46763      
46764     /**
46765      * @cfg {Object} baseParams
46766      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46767      */
46768      /**
46769      
46770     /**
46771      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46772      */
46773     timeout: 30,
46774
46775     // private
46776     activeAction : null,
46777
46778     /**
46779      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46780      * or setValues() data instead of when the form was first created.
46781      */
46782     trackResetOnLoad : false,
46783     
46784     
46785     /**
46786      * childForms - used for multi-tab forms
46787      * @type {Array}
46788      */
46789     childForms : false,
46790     
46791     /**
46792      * allItems - full list of fields.
46793      * @type {Array}
46794      */
46795     allItems : false,
46796     
46797     /**
46798      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46799      * element by passing it or its id or mask the form itself by passing in true.
46800      * @type Mixed
46801      */
46802     waitMsgTarget : false,
46803
46804     // private
46805     initEl : function(el){
46806         this.el = Roo.get(el);
46807         this.id = this.el.id || Roo.id();
46808         this.el.on('submit', this.onSubmit, this);
46809         this.el.addClass('x-form');
46810     },
46811
46812     // private
46813     onSubmit : function(e){
46814         e.stopEvent();
46815     },
46816
46817     /**
46818      * Returns true if client-side validation on the form is successful.
46819      * @return Boolean
46820      */
46821     isValid : function(){
46822         var valid = true;
46823         this.items.each(function(f){
46824            if(!f.validate()){
46825                valid = false;
46826            }
46827         });
46828         return valid;
46829     },
46830
46831     /**
46832      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
46833      * @return Boolean
46834      */
46835     isDirty : function(){
46836         var dirty = false;
46837         this.items.each(function(f){
46838            if(f.isDirty()){
46839                dirty = true;
46840                return false;
46841            }
46842         });
46843         return dirty;
46844     },
46845     
46846     /**
46847      * Returns true if any fields in this form have changed since their original load. (New version)
46848      * @return Boolean
46849      */
46850     
46851     hasChanged : function()
46852     {
46853         var dirty = false;
46854         this.items.each(function(f){
46855            if(f.hasChanged()){
46856                dirty = true;
46857                return false;
46858            }
46859         });
46860         return dirty;
46861         
46862     },
46863     /**
46864      * Resets all hasChanged to 'false' -
46865      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
46866      * So hasChanged storage is only to be used for this purpose
46867      * @return Boolean
46868      */
46869     resetHasChanged : function()
46870     {
46871         this.items.each(function(f){
46872            f.resetHasChanged();
46873         });
46874         
46875     },
46876     
46877     
46878     /**
46879      * Performs a predefined action (submit or load) or custom actions you define on this form.
46880      * @param {String} actionName The name of the action type
46881      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
46882      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
46883      * accept other config options):
46884      * <pre>
46885 Property          Type             Description
46886 ----------------  ---------------  ----------------------------------------------------------------------------------
46887 url               String           The url for the action (defaults to the form's url)
46888 method            String           The form method to use (defaults to the form's method, or POST if not defined)
46889 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
46890 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
46891                                    validate the form on the client (defaults to false)
46892      * </pre>
46893      * @return {BasicForm} this
46894      */
46895     doAction : function(action, options){
46896         if(typeof action == 'string'){
46897             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
46898         }
46899         if(this.fireEvent('beforeaction', this, action) !== false){
46900             this.beforeAction(action);
46901             action.run.defer(100, action);
46902         }
46903         return this;
46904     },
46905
46906     /**
46907      * Shortcut to do a submit action.
46908      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46909      * @return {BasicForm} this
46910      */
46911     submit : function(options){
46912         this.doAction('submit', options);
46913         return this;
46914     },
46915
46916     /**
46917      * Shortcut to do a load action.
46918      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46919      * @return {BasicForm} this
46920      */
46921     load : function(options){
46922         this.doAction('load', options);
46923         return this;
46924     },
46925
46926     /**
46927      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
46928      * @param {Record} record The record to edit
46929      * @return {BasicForm} this
46930      */
46931     updateRecord : function(record){
46932         record.beginEdit();
46933         var fs = record.fields;
46934         fs.each(function(f){
46935             var field = this.findField(f.name);
46936             if(field){
46937                 record.set(f.name, field.getValue());
46938             }
46939         }, this);
46940         record.endEdit();
46941         return this;
46942     },
46943
46944     /**
46945      * Loads an Roo.data.Record into this form.
46946      * @param {Record} record The record to load
46947      * @return {BasicForm} this
46948      */
46949     loadRecord : function(record){
46950         this.setValues(record.data);
46951         return this;
46952     },
46953
46954     // private
46955     beforeAction : function(action){
46956         var o = action.options;
46957         
46958        
46959         if(this.waitMsgTarget === true){
46960             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
46961         }else if(this.waitMsgTarget){
46962             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
46963             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
46964         }else {
46965             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
46966         }
46967          
46968     },
46969
46970     // private
46971     afterAction : function(action, success){
46972         this.activeAction = null;
46973         var o = action.options;
46974         
46975         if(this.waitMsgTarget === true){
46976             this.el.unmask();
46977         }else if(this.waitMsgTarget){
46978             this.waitMsgTarget.unmask();
46979         }else{
46980             Roo.MessageBox.updateProgress(1);
46981             Roo.MessageBox.hide();
46982         }
46983          
46984         if(success){
46985             if(o.reset){
46986                 this.reset();
46987             }
46988             Roo.callback(o.success, o.scope, [this, action]);
46989             this.fireEvent('actioncomplete', this, action);
46990             
46991         }else{
46992             
46993             // failure condition..
46994             // we have a scenario where updates need confirming.
46995             // eg. if a locking scenario exists..
46996             // we look for { errors : { needs_confirm : true }} in the response.
46997             if (
46998                 (typeof(action.result) != 'undefined')  &&
46999                 (typeof(action.result.errors) != 'undefined')  &&
47000                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47001            ){
47002                 var _t = this;
47003                 Roo.MessageBox.confirm(
47004                     "Change requires confirmation",
47005                     action.result.errorMsg,
47006                     function(r) {
47007                         if (r != 'yes') {
47008                             return;
47009                         }
47010                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47011                     }
47012                     
47013                 );
47014                 
47015                 
47016                 
47017                 return;
47018             }
47019             
47020             Roo.callback(o.failure, o.scope, [this, action]);
47021             // show an error message if no failed handler is set..
47022             if (!this.hasListener('actionfailed')) {
47023                 Roo.MessageBox.alert("Error",
47024                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47025                         action.result.errorMsg :
47026                         "Saving Failed, please check your entries or try again"
47027                 );
47028             }
47029             
47030             this.fireEvent('actionfailed', this, action);
47031         }
47032         
47033     },
47034
47035     /**
47036      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47037      * @param {String} id The value to search for
47038      * @return Field
47039      */
47040     findField : function(id){
47041         var field = this.items.get(id);
47042         if(!field){
47043             this.items.each(function(f){
47044                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47045                     field = f;
47046                     return false;
47047                 }
47048             });
47049         }
47050         return field || null;
47051     },
47052
47053     /**
47054      * Add a secondary form to this one, 
47055      * Used to provide tabbed forms. One form is primary, with hidden values 
47056      * which mirror the elements from the other forms.
47057      * 
47058      * @param {Roo.form.Form} form to add.
47059      * 
47060      */
47061     addForm : function(form)
47062     {
47063        
47064         if (this.childForms.indexOf(form) > -1) {
47065             // already added..
47066             return;
47067         }
47068         this.childForms.push(form);
47069         var n = '';
47070         Roo.each(form.allItems, function (fe) {
47071             
47072             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47073             if (this.findField(n)) { // already added..
47074                 return;
47075             }
47076             var add = new Roo.form.Hidden({
47077                 name : n
47078             });
47079             add.render(this.el);
47080             
47081             this.add( add );
47082         }, this);
47083         
47084     },
47085     /**
47086      * Mark fields in this form invalid in bulk.
47087      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47088      * @return {BasicForm} this
47089      */
47090     markInvalid : function(errors){
47091         if(errors instanceof Array){
47092             for(var i = 0, len = errors.length; i < len; i++){
47093                 var fieldError = errors[i];
47094                 var f = this.findField(fieldError.id);
47095                 if(f){
47096                     f.markInvalid(fieldError.msg);
47097                 }
47098             }
47099         }else{
47100             var field, id;
47101             for(id in errors){
47102                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47103                     field.markInvalid(errors[id]);
47104                 }
47105             }
47106         }
47107         Roo.each(this.childForms || [], function (f) {
47108             f.markInvalid(errors);
47109         });
47110         
47111         return this;
47112     },
47113
47114     /**
47115      * Set values for fields in this form in bulk.
47116      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
47117      * @return {BasicForm} this
47118      */
47119     setValues : function(values){
47120         if(values instanceof Array){ // array of objects
47121             for(var i = 0, len = values.length; i < len; i++){
47122                 var v = values[i];
47123                 var f = this.findField(v.id);
47124                 if(f){
47125                     f.setValue(v.value);
47126                     if(this.trackResetOnLoad){
47127                         f.originalValue = f.getValue();
47128                     }
47129                 }
47130             }
47131         }else{ // object hash
47132             var field, id;
47133             for(id in values){
47134                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47135                     
47136                     if (field.setFromData && 
47137                         field.valueField && 
47138                         field.displayField &&
47139                         // combos' with local stores can 
47140                         // be queried via setValue()
47141                         // to set their value..
47142                         (field.store && !field.store.isLocal)
47143                         ) {
47144                         // it's a combo
47145                         var sd = { };
47146                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47147                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47148                         field.setFromData(sd);
47149                         
47150                     } else {
47151                         field.setValue(values[id]);
47152                     }
47153                     
47154                     
47155                     if(this.trackResetOnLoad){
47156                         field.originalValue = field.getValue();
47157                     }
47158                 }
47159             }
47160         }
47161         this.resetHasChanged();
47162         
47163         
47164         Roo.each(this.childForms || [], function (f) {
47165             f.setValues(values);
47166             f.resetHasChanged();
47167         });
47168                 
47169         return this;
47170     },
47171
47172     /**
47173      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47174      * they are returned as an array.
47175      * @param {Boolean} asString
47176      * @return {Object}
47177      */
47178     getValues : function(asString){
47179         if (this.childForms) {
47180             // copy values from the child forms
47181             Roo.each(this.childForms, function (f) {
47182                 this.setValues(f.getValues());
47183             }, this);
47184         }
47185         
47186         
47187         
47188         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47189         if(asString === true){
47190             return fs;
47191         }
47192         return Roo.urlDecode(fs);
47193     },
47194     
47195     /**
47196      * Returns the fields in this form as an object with key/value pairs. 
47197      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47198      * @return {Object}
47199      */
47200     getFieldValues : function(with_hidden)
47201     {
47202         if (this.childForms) {
47203             // copy values from the child forms
47204             // should this call getFieldValues - probably not as we do not currently copy
47205             // hidden fields when we generate..
47206             Roo.each(this.childForms, function (f) {
47207                 this.setValues(f.getValues());
47208             }, this);
47209         }
47210         
47211         var ret = {};
47212         this.items.each(function(f){
47213             if (!f.getName()) {
47214                 return;
47215             }
47216             var v = f.getValue();
47217             if (f.inputType =='radio') {
47218                 if (typeof(ret[f.getName()]) == 'undefined') {
47219                     ret[f.getName()] = ''; // empty..
47220                 }
47221                 
47222                 if (!f.el.dom.checked) {
47223                     return;
47224                     
47225                 }
47226                 v = f.el.dom.value;
47227                 
47228             }
47229             
47230             // not sure if this supported any more..
47231             if ((typeof(v) == 'object') && f.getRawValue) {
47232                 v = f.getRawValue() ; // dates..
47233             }
47234             // combo boxes where name != hiddenName...
47235             if (f.name != f.getName()) {
47236                 ret[f.name] = f.getRawValue();
47237             }
47238             ret[f.getName()] = v;
47239         });
47240         
47241         return ret;
47242     },
47243
47244     /**
47245      * Clears all invalid messages in this form.
47246      * @return {BasicForm} this
47247      */
47248     clearInvalid : function(){
47249         this.items.each(function(f){
47250            f.clearInvalid();
47251         });
47252         
47253         Roo.each(this.childForms || [], function (f) {
47254             f.clearInvalid();
47255         });
47256         
47257         
47258         return this;
47259     },
47260
47261     /**
47262      * Resets this form.
47263      * @return {BasicForm} this
47264      */
47265     reset : function(){
47266         this.items.each(function(f){
47267             f.reset();
47268         });
47269         
47270         Roo.each(this.childForms || [], function (f) {
47271             f.reset();
47272         });
47273         this.resetHasChanged();
47274         
47275         return this;
47276     },
47277
47278     /**
47279      * Add Roo.form components to this form.
47280      * @param {Field} field1
47281      * @param {Field} field2 (optional)
47282      * @param {Field} etc (optional)
47283      * @return {BasicForm} this
47284      */
47285     add : function(){
47286         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47287         return this;
47288     },
47289
47290
47291     /**
47292      * Removes a field from the items collection (does NOT remove its markup).
47293      * @param {Field} field
47294      * @return {BasicForm} this
47295      */
47296     remove : function(field){
47297         this.items.remove(field);
47298         return this;
47299     },
47300
47301     /**
47302      * Looks at the fields in this form, checks them for an id attribute,
47303      * and calls applyTo on the existing dom element with that id.
47304      * @return {BasicForm} this
47305      */
47306     render : function(){
47307         this.items.each(function(f){
47308             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47309                 f.applyTo(f.id);
47310             }
47311         });
47312         return this;
47313     },
47314
47315     /**
47316      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47317      * @param {Object} values
47318      * @return {BasicForm} this
47319      */
47320     applyToFields : function(o){
47321         this.items.each(function(f){
47322            Roo.apply(f, o);
47323         });
47324         return this;
47325     },
47326
47327     /**
47328      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47329      * @param {Object} values
47330      * @return {BasicForm} this
47331      */
47332     applyIfToFields : function(o){
47333         this.items.each(function(f){
47334            Roo.applyIf(f, o);
47335         });
47336         return this;
47337     }
47338 });
47339
47340 // back compat
47341 Roo.BasicForm = Roo.form.BasicForm;/*
47342  * Based on:
47343  * Ext JS Library 1.1.1
47344  * Copyright(c) 2006-2007, Ext JS, LLC.
47345  *
47346  * Originally Released Under LGPL - original licence link has changed is not relivant.
47347  *
47348  * Fork - LGPL
47349  * <script type="text/javascript">
47350  */
47351
47352 /**
47353  * @class Roo.form.Form
47354  * @extends Roo.form.BasicForm
47355  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47356  * @constructor
47357  * @param {Object} config Configuration options
47358  */
47359 Roo.form.Form = function(config){
47360     var xitems =  [];
47361     if (config.items) {
47362         xitems = config.items;
47363         delete config.items;
47364     }
47365    
47366     
47367     Roo.form.Form.superclass.constructor.call(this, null, config);
47368     this.url = this.url || this.action;
47369     if(!this.root){
47370         this.root = new Roo.form.Layout(Roo.applyIf({
47371             id: Roo.id()
47372         }, config));
47373     }
47374     this.active = this.root;
47375     /**
47376      * Array of all the buttons that have been added to this form via {@link addButton}
47377      * @type Array
47378      */
47379     this.buttons = [];
47380     this.allItems = [];
47381     this.addEvents({
47382         /**
47383          * @event clientvalidation
47384          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47385          * @param {Form} this
47386          * @param {Boolean} valid true if the form has passed client-side validation
47387          */
47388         clientvalidation: true,
47389         /**
47390          * @event rendered
47391          * Fires when the form is rendered
47392          * @param {Roo.form.Form} form
47393          */
47394         rendered : true
47395     });
47396     
47397     if (this.progressUrl) {
47398             // push a hidden field onto the list of fields..
47399             this.addxtype( {
47400                     xns: Roo.form, 
47401                     xtype : 'Hidden', 
47402                     name : 'UPLOAD_IDENTIFIER' 
47403             });
47404         }
47405         
47406     
47407     Roo.each(xitems, this.addxtype, this);
47408     
47409     
47410     
47411 };
47412
47413 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47414     /**
47415      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47416      */
47417     /**
47418      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47419      */
47420     /**
47421      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47422      */
47423     buttonAlign:'center',
47424
47425     /**
47426      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47427      */
47428     minButtonWidth:75,
47429
47430     /**
47431      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47432      * This property cascades to child containers if not set.
47433      */
47434     labelAlign:'left',
47435
47436     /**
47437      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47438      * fires a looping event with that state. This is required to bind buttons to the valid
47439      * state using the config value formBind:true on the button.
47440      */
47441     monitorValid : false,
47442
47443     /**
47444      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47445      */
47446     monitorPoll : 200,
47447     
47448     /**
47449      * @cfg {String} progressUrl - Url to return progress data 
47450      */
47451     
47452     progressUrl : false,
47453     /**
47454      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
47455      * sending a formdata with extra parameters - eg uploaded elements.
47456      */
47457     
47458     formData : false,
47459     
47460     /**
47461      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47462      * fields are added and the column is closed. If no fields are passed the column remains open
47463      * until end() is called.
47464      * @param {Object} config The config to pass to the column
47465      * @param {Field} field1 (optional)
47466      * @param {Field} field2 (optional)
47467      * @param {Field} etc (optional)
47468      * @return Column The column container object
47469      */
47470     column : function(c){
47471         var col = new Roo.form.Column(c);
47472         this.start(col);
47473         if(arguments.length > 1){ // duplicate code required because of Opera
47474             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47475             this.end();
47476         }
47477         return col;
47478     },
47479
47480     /**
47481      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47482      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47483      * until end() is called.
47484      * @param {Object} config The config to pass to the fieldset
47485      * @param {Field} field1 (optional)
47486      * @param {Field} field2 (optional)
47487      * @param {Field} etc (optional)
47488      * @return FieldSet The fieldset container object
47489      */
47490     fieldset : function(c){
47491         var fs = new Roo.form.FieldSet(c);
47492         this.start(fs);
47493         if(arguments.length > 1){ // duplicate code required because of Opera
47494             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47495             this.end();
47496         }
47497         return fs;
47498     },
47499
47500     /**
47501      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47502      * fields are added and the container is closed. If no fields are passed the container remains open
47503      * until end() is called.
47504      * @param {Object} config The config to pass to the Layout
47505      * @param {Field} field1 (optional)
47506      * @param {Field} field2 (optional)
47507      * @param {Field} etc (optional)
47508      * @return Layout The container object
47509      */
47510     container : function(c){
47511         var l = new Roo.form.Layout(c);
47512         this.start(l);
47513         if(arguments.length > 1){ // duplicate code required because of Opera
47514             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47515             this.end();
47516         }
47517         return l;
47518     },
47519
47520     /**
47521      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47522      * @param {Object} container A Roo.form.Layout or subclass of Layout
47523      * @return {Form} this
47524      */
47525     start : function(c){
47526         // cascade label info
47527         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47528         this.active.stack.push(c);
47529         c.ownerCt = this.active;
47530         this.active = c;
47531         return this;
47532     },
47533
47534     /**
47535      * Closes the current open container
47536      * @return {Form} this
47537      */
47538     end : function(){
47539         if(this.active == this.root){
47540             return this;
47541         }
47542         this.active = this.active.ownerCt;
47543         return this;
47544     },
47545
47546     /**
47547      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47548      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47549      * as the label of the field.
47550      * @param {Field} field1
47551      * @param {Field} field2 (optional)
47552      * @param {Field} etc. (optional)
47553      * @return {Form} this
47554      */
47555     add : function(){
47556         this.active.stack.push.apply(this.active.stack, arguments);
47557         this.allItems.push.apply(this.allItems,arguments);
47558         var r = [];
47559         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47560             if(a[i].isFormField){
47561                 r.push(a[i]);
47562             }
47563         }
47564         if(r.length > 0){
47565             Roo.form.Form.superclass.add.apply(this, r);
47566         }
47567         return this;
47568     },
47569     
47570
47571     
47572     
47573     
47574      /**
47575      * Find any element that has been added to a form, using it's ID or name
47576      * This can include framesets, columns etc. along with regular fields..
47577      * @param {String} id - id or name to find.
47578      
47579      * @return {Element} e - or false if nothing found.
47580      */
47581     findbyId : function(id)
47582     {
47583         var ret = false;
47584         if (!id) {
47585             return ret;
47586         }
47587         Roo.each(this.allItems, function(f){
47588             if (f.id == id || f.name == id ){
47589                 ret = f;
47590                 return false;
47591             }
47592         });
47593         return ret;
47594     },
47595
47596     
47597     
47598     /**
47599      * Render this form into the passed container. This should only be called once!
47600      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47601      * @return {Form} this
47602      */
47603     render : function(ct)
47604     {
47605         
47606         
47607         
47608         ct = Roo.get(ct);
47609         var o = this.autoCreate || {
47610             tag: 'form',
47611             method : this.method || 'POST',
47612             id : this.id || Roo.id()
47613         };
47614         this.initEl(ct.createChild(o));
47615
47616         this.root.render(this.el);
47617         
47618        
47619              
47620         this.items.each(function(f){
47621             f.render('x-form-el-'+f.id);
47622         });
47623
47624         if(this.buttons.length > 0){
47625             // tables are required to maintain order and for correct IE layout
47626             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47627                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47628                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47629             }}, null, true);
47630             var tr = tb.getElementsByTagName('tr')[0];
47631             for(var i = 0, len = this.buttons.length; i < len; i++) {
47632                 var b = this.buttons[i];
47633                 var td = document.createElement('td');
47634                 td.className = 'x-form-btn-td';
47635                 b.render(tr.appendChild(td));
47636             }
47637         }
47638         if(this.monitorValid){ // initialize after render
47639             this.startMonitoring();
47640         }
47641         this.fireEvent('rendered', this);
47642         return this;
47643     },
47644
47645     /**
47646      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
47647      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
47648      * object or a valid Roo.DomHelper element config
47649      * @param {Function} handler The function called when the button is clicked
47650      * @param {Object} scope (optional) The scope of the handler function
47651      * @return {Roo.Button}
47652      */
47653     addButton : function(config, handler, scope){
47654         var bc = {
47655             handler: handler,
47656             scope: scope,
47657             minWidth: this.minButtonWidth,
47658             hideParent:true
47659         };
47660         if(typeof config == "string"){
47661             bc.text = config;
47662         }else{
47663             Roo.apply(bc, config);
47664         }
47665         var btn = new Roo.Button(null, bc);
47666         this.buttons.push(btn);
47667         return btn;
47668     },
47669
47670      /**
47671      * Adds a series of form elements (using the xtype property as the factory method.
47672      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
47673      * @param {Object} config 
47674      */
47675     
47676     addxtype : function()
47677     {
47678         var ar = Array.prototype.slice.call(arguments, 0);
47679         var ret = false;
47680         for(var i = 0; i < ar.length; i++) {
47681             if (!ar[i]) {
47682                 continue; // skip -- if this happends something invalid got sent, we 
47683                 // should ignore it, as basically that interface element will not show up
47684                 // and that should be pretty obvious!!
47685             }
47686             
47687             if (Roo.form[ar[i].xtype]) {
47688                 ar[i].form = this;
47689                 var fe = Roo.factory(ar[i], Roo.form);
47690                 if (!ret) {
47691                     ret = fe;
47692                 }
47693                 fe.form = this;
47694                 if (fe.store) {
47695                     fe.store.form = this;
47696                 }
47697                 if (fe.isLayout) {  
47698                          
47699                     this.start(fe);
47700                     this.allItems.push(fe);
47701                     if (fe.items && fe.addxtype) {
47702                         fe.addxtype.apply(fe, fe.items);
47703                         delete fe.items;
47704                     }
47705                      this.end();
47706                     continue;
47707                 }
47708                 
47709                 
47710                  
47711                 this.add(fe);
47712               //  console.log('adding ' + ar[i].xtype);
47713             }
47714             if (ar[i].xtype == 'Button') {  
47715                 //console.log('adding button');
47716                 //console.log(ar[i]);
47717                 this.addButton(ar[i]);
47718                 this.allItems.push(fe);
47719                 continue;
47720             }
47721             
47722             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
47723                 alert('end is not supported on xtype any more, use items');
47724             //    this.end();
47725             //    //console.log('adding end');
47726             }
47727             
47728         }
47729         return ret;
47730     },
47731     
47732     /**
47733      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
47734      * option "monitorValid"
47735      */
47736     startMonitoring : function(){
47737         if(!this.bound){
47738             this.bound = true;
47739             Roo.TaskMgr.start({
47740                 run : this.bindHandler,
47741                 interval : this.monitorPoll || 200,
47742                 scope: this
47743             });
47744         }
47745     },
47746
47747     /**
47748      * Stops monitoring of the valid state of this form
47749      */
47750     stopMonitoring : function(){
47751         this.bound = false;
47752     },
47753
47754     // private
47755     bindHandler : function(){
47756         if(!this.bound){
47757             return false; // stops binding
47758         }
47759         var valid = true;
47760         this.items.each(function(f){
47761             if(!f.isValid(true)){
47762                 valid = false;
47763                 return false;
47764             }
47765         });
47766         for(var i = 0, len = this.buttons.length; i < len; i++){
47767             var btn = this.buttons[i];
47768             if(btn.formBind === true && btn.disabled === valid){
47769                 btn.setDisabled(!valid);
47770             }
47771         }
47772         this.fireEvent('clientvalidation', this, valid);
47773     }
47774     
47775     
47776     
47777     
47778     
47779     
47780     
47781     
47782 });
47783
47784
47785 // back compat
47786 Roo.Form = Roo.form.Form;
47787 /*
47788  * Based on:
47789  * Ext JS Library 1.1.1
47790  * Copyright(c) 2006-2007, Ext JS, LLC.
47791  *
47792  * Originally Released Under LGPL - original licence link has changed is not relivant.
47793  *
47794  * Fork - LGPL
47795  * <script type="text/javascript">
47796  */
47797
47798 // as we use this in bootstrap.
47799 Roo.namespace('Roo.form');
47800  /**
47801  * @class Roo.form.Action
47802  * Internal Class used to handle form actions
47803  * @constructor
47804  * @param {Roo.form.BasicForm} el The form element or its id
47805  * @param {Object} config Configuration options
47806  */
47807
47808  
47809  
47810 // define the action interface
47811 Roo.form.Action = function(form, options){
47812     this.form = form;
47813     this.options = options || {};
47814 };
47815 /**
47816  * Client Validation Failed
47817  * @const 
47818  */
47819 Roo.form.Action.CLIENT_INVALID = 'client';
47820 /**
47821  * Server Validation Failed
47822  * @const 
47823  */
47824 Roo.form.Action.SERVER_INVALID = 'server';
47825  /**
47826  * Connect to Server Failed
47827  * @const 
47828  */
47829 Roo.form.Action.CONNECT_FAILURE = 'connect';
47830 /**
47831  * Reading Data from Server Failed
47832  * @const 
47833  */
47834 Roo.form.Action.LOAD_FAILURE = 'load';
47835
47836 Roo.form.Action.prototype = {
47837     type : 'default',
47838     failureType : undefined,
47839     response : undefined,
47840     result : undefined,
47841
47842     // interface method
47843     run : function(options){
47844
47845     },
47846
47847     // interface method
47848     success : function(response){
47849
47850     },
47851
47852     // interface method
47853     handleResponse : function(response){
47854
47855     },
47856
47857     // default connection failure
47858     failure : function(response){
47859         
47860         this.response = response;
47861         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47862         this.form.afterAction(this, false);
47863     },
47864
47865     processResponse : function(response){
47866         this.response = response;
47867         if(!response.responseText){
47868             return true;
47869         }
47870         this.result = this.handleResponse(response);
47871         return this.result;
47872     },
47873
47874     // utility functions used internally
47875     getUrl : function(appendParams){
47876         var url = this.options.url || this.form.url || this.form.el.dom.action;
47877         if(appendParams){
47878             var p = this.getParams();
47879             if(p){
47880                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
47881             }
47882         }
47883         return url;
47884     },
47885
47886     getMethod : function(){
47887         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
47888     },
47889
47890     getParams : function(){
47891         var bp = this.form.baseParams;
47892         var p = this.options.params;
47893         if(p){
47894             if(typeof p == "object"){
47895                 p = Roo.urlEncode(Roo.applyIf(p, bp));
47896             }else if(typeof p == 'string' && bp){
47897                 p += '&' + Roo.urlEncode(bp);
47898             }
47899         }else if(bp){
47900             p = Roo.urlEncode(bp);
47901         }
47902         return p;
47903     },
47904
47905     createCallback : function(){
47906         return {
47907             success: this.success,
47908             failure: this.failure,
47909             scope: this,
47910             timeout: (this.form.timeout*1000),
47911             upload: this.form.fileUpload ? this.success : undefined
47912         };
47913     }
47914 };
47915
47916 Roo.form.Action.Submit = function(form, options){
47917     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
47918 };
47919
47920 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
47921     type : 'submit',
47922
47923     haveProgress : false,
47924     uploadComplete : false,
47925     
47926     // uploadProgress indicator.
47927     uploadProgress : function()
47928     {
47929         if (!this.form.progressUrl) {
47930             return;
47931         }
47932         
47933         if (!this.haveProgress) {
47934             Roo.MessageBox.progress("Uploading", "Uploading");
47935         }
47936         if (this.uploadComplete) {
47937            Roo.MessageBox.hide();
47938            return;
47939         }
47940         
47941         this.haveProgress = true;
47942    
47943         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
47944         
47945         var c = new Roo.data.Connection();
47946         c.request({
47947             url : this.form.progressUrl,
47948             params: {
47949                 id : uid
47950             },
47951             method: 'GET',
47952             success : function(req){
47953                //console.log(data);
47954                 var rdata = false;
47955                 var edata;
47956                 try  {
47957                    rdata = Roo.decode(req.responseText)
47958                 } catch (e) {
47959                     Roo.log("Invalid data from server..");
47960                     Roo.log(edata);
47961                     return;
47962                 }
47963                 if (!rdata || !rdata.success) {
47964                     Roo.log(rdata);
47965                     Roo.MessageBox.alert(Roo.encode(rdata));
47966                     return;
47967                 }
47968                 var data = rdata.data;
47969                 
47970                 if (this.uploadComplete) {
47971                    Roo.MessageBox.hide();
47972                    return;
47973                 }
47974                    
47975                 if (data){
47976                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
47977                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
47978                     );
47979                 }
47980                 this.uploadProgress.defer(2000,this);
47981             },
47982        
47983             failure: function(data) {
47984                 Roo.log('progress url failed ');
47985                 Roo.log(data);
47986             },
47987             scope : this
47988         });
47989            
47990     },
47991     
47992     
47993     run : function()
47994     {
47995         // run get Values on the form, so it syncs any secondary forms.
47996         this.form.getValues();
47997         
47998         var o = this.options;
47999         var method = this.getMethod();
48000         var isPost = method == 'POST';
48001         if(o.clientValidation === false || this.form.isValid()){
48002             
48003             if (this.form.progressUrl) {
48004                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
48005                     (new Date() * 1) + '' + Math.random());
48006                     
48007             } 
48008             
48009             
48010             Roo.Ajax.request(Roo.apply(this.createCallback(), {
48011                 form:this.form.el.dom,
48012                 url:this.getUrl(!isPost),
48013                 method: method,
48014                 params:isPost ? this.getParams() : null,
48015                 isUpload: this.form.fileUpload,
48016                 formData : this.form.formData
48017             }));
48018             
48019             this.uploadProgress();
48020
48021         }else if (o.clientValidation !== false){ // client validation failed
48022             this.failureType = Roo.form.Action.CLIENT_INVALID;
48023             this.form.afterAction(this, false);
48024         }
48025     },
48026
48027     success : function(response)
48028     {
48029         this.uploadComplete= true;
48030         if (this.haveProgress) {
48031             Roo.MessageBox.hide();
48032         }
48033         
48034         
48035         var result = this.processResponse(response);
48036         if(result === true || result.success){
48037             this.form.afterAction(this, true);
48038             return;
48039         }
48040         if(result.errors){
48041             this.form.markInvalid(result.errors);
48042             this.failureType = Roo.form.Action.SERVER_INVALID;
48043         }
48044         this.form.afterAction(this, false);
48045     },
48046     failure : function(response)
48047     {
48048         this.uploadComplete= true;
48049         if (this.haveProgress) {
48050             Roo.MessageBox.hide();
48051         }
48052         
48053         this.response = response;
48054         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48055         this.form.afterAction(this, false);
48056     },
48057     
48058     handleResponse : function(response){
48059         if(this.form.errorReader){
48060             var rs = this.form.errorReader.read(response);
48061             var errors = [];
48062             if(rs.records){
48063                 for(var i = 0, len = rs.records.length; i < len; i++) {
48064                     var r = rs.records[i];
48065                     errors[i] = r.data;
48066                 }
48067             }
48068             if(errors.length < 1){
48069                 errors = null;
48070             }
48071             return {
48072                 success : rs.success,
48073                 errors : errors
48074             };
48075         }
48076         var ret = false;
48077         try {
48078             ret = Roo.decode(response.responseText);
48079         } catch (e) {
48080             ret = {
48081                 success: false,
48082                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
48083                 errors : []
48084             };
48085         }
48086         return ret;
48087         
48088     }
48089 });
48090
48091
48092 Roo.form.Action.Load = function(form, options){
48093     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
48094     this.reader = this.form.reader;
48095 };
48096
48097 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
48098     type : 'load',
48099
48100     run : function(){
48101         
48102         Roo.Ajax.request(Roo.apply(
48103                 this.createCallback(), {
48104                     method:this.getMethod(),
48105                     url:this.getUrl(false),
48106                     params:this.getParams()
48107         }));
48108     },
48109
48110     success : function(response){
48111         
48112         var result = this.processResponse(response);
48113         if(result === true || !result.success || !result.data){
48114             this.failureType = Roo.form.Action.LOAD_FAILURE;
48115             this.form.afterAction(this, false);
48116             return;
48117         }
48118         this.form.clearInvalid();
48119         this.form.setValues(result.data);
48120         this.form.afterAction(this, true);
48121     },
48122
48123     handleResponse : function(response){
48124         if(this.form.reader){
48125             var rs = this.form.reader.read(response);
48126             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
48127             return {
48128                 success : rs.success,
48129                 data : data
48130             };
48131         }
48132         return Roo.decode(response.responseText);
48133     }
48134 });
48135
48136 Roo.form.Action.ACTION_TYPES = {
48137     'load' : Roo.form.Action.Load,
48138     'submit' : Roo.form.Action.Submit
48139 };/*
48140  * Based on:
48141  * Ext JS Library 1.1.1
48142  * Copyright(c) 2006-2007, Ext JS, LLC.
48143  *
48144  * Originally Released Under LGPL - original licence link has changed is not relivant.
48145  *
48146  * Fork - LGPL
48147  * <script type="text/javascript">
48148  */
48149  
48150 /**
48151  * @class Roo.form.Layout
48152  * @extends Roo.Component
48153  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
48154  * @constructor
48155  * @param {Object} config Configuration options
48156  */
48157 Roo.form.Layout = function(config){
48158     var xitems = [];
48159     if (config.items) {
48160         xitems = config.items;
48161         delete config.items;
48162     }
48163     Roo.form.Layout.superclass.constructor.call(this, config);
48164     this.stack = [];
48165     Roo.each(xitems, this.addxtype, this);
48166      
48167 };
48168
48169 Roo.extend(Roo.form.Layout, Roo.Component, {
48170     /**
48171      * @cfg {String/Object} autoCreate
48172      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48173      */
48174     /**
48175      * @cfg {String/Object/Function} style
48176      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48177      * a function which returns such a specification.
48178      */
48179     /**
48180      * @cfg {String} labelAlign
48181      * Valid values are "left," "top" and "right" (defaults to "left")
48182      */
48183     /**
48184      * @cfg {Number} labelWidth
48185      * Fixed width in pixels of all field labels (defaults to undefined)
48186      */
48187     /**
48188      * @cfg {Boolean} clear
48189      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48190      */
48191     clear : true,
48192     /**
48193      * @cfg {String} labelSeparator
48194      * The separator to use after field labels (defaults to ':')
48195      */
48196     labelSeparator : ':',
48197     /**
48198      * @cfg {Boolean} hideLabels
48199      * True to suppress the display of field labels in this layout (defaults to false)
48200      */
48201     hideLabels : false,
48202
48203     // private
48204     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48205     
48206     isLayout : true,
48207     
48208     // private
48209     onRender : function(ct, position){
48210         if(this.el){ // from markup
48211             this.el = Roo.get(this.el);
48212         }else {  // generate
48213             var cfg = this.getAutoCreate();
48214             this.el = ct.createChild(cfg, position);
48215         }
48216         if(this.style){
48217             this.el.applyStyles(this.style);
48218         }
48219         if(this.labelAlign){
48220             this.el.addClass('x-form-label-'+this.labelAlign);
48221         }
48222         if(this.hideLabels){
48223             this.labelStyle = "display:none";
48224             this.elementStyle = "padding-left:0;";
48225         }else{
48226             if(typeof this.labelWidth == 'number'){
48227                 this.labelStyle = "width:"+this.labelWidth+"px;";
48228                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48229             }
48230             if(this.labelAlign == 'top'){
48231                 this.labelStyle = "width:auto;";
48232                 this.elementStyle = "padding-left:0;";
48233             }
48234         }
48235         var stack = this.stack;
48236         var slen = stack.length;
48237         if(slen > 0){
48238             if(!this.fieldTpl){
48239                 var t = new Roo.Template(
48240                     '<div class="x-form-item {5}">',
48241                         '<label for="{0}" style="{2}">{1}{4}</label>',
48242                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48243                         '</div>',
48244                     '</div><div class="x-form-clear-left"></div>'
48245                 );
48246                 t.disableFormats = true;
48247                 t.compile();
48248                 Roo.form.Layout.prototype.fieldTpl = t;
48249             }
48250             for(var i = 0; i < slen; i++) {
48251                 if(stack[i].isFormField){
48252                     this.renderField(stack[i]);
48253                 }else{
48254                     this.renderComponent(stack[i]);
48255                 }
48256             }
48257         }
48258         if(this.clear){
48259             this.el.createChild({cls:'x-form-clear'});
48260         }
48261     },
48262
48263     // private
48264     renderField : function(f){
48265         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48266                f.id, //0
48267                f.fieldLabel, //1
48268                f.labelStyle||this.labelStyle||'', //2
48269                this.elementStyle||'', //3
48270                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48271                f.itemCls||this.itemCls||''  //5
48272        ], true).getPrevSibling());
48273     },
48274
48275     // private
48276     renderComponent : function(c){
48277         c.render(c.isLayout ? this.el : this.el.createChild());    
48278     },
48279     /**
48280      * Adds a object form elements (using the xtype property as the factory method.)
48281      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48282      * @param {Object} config 
48283      */
48284     addxtype : function(o)
48285     {
48286         // create the lement.
48287         o.form = this.form;
48288         var fe = Roo.factory(o, Roo.form);
48289         this.form.allItems.push(fe);
48290         this.stack.push(fe);
48291         
48292         if (fe.isFormField) {
48293             this.form.items.add(fe);
48294         }
48295          
48296         return fe;
48297     }
48298 });
48299
48300 /**
48301  * @class Roo.form.Column
48302  * @extends Roo.form.Layout
48303  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48304  * @constructor
48305  * @param {Object} config Configuration options
48306  */
48307 Roo.form.Column = function(config){
48308     Roo.form.Column.superclass.constructor.call(this, config);
48309 };
48310
48311 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48312     /**
48313      * @cfg {Number/String} width
48314      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48315      */
48316     /**
48317      * @cfg {String/Object} autoCreate
48318      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48319      */
48320
48321     // private
48322     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48323
48324     // private
48325     onRender : function(ct, position){
48326         Roo.form.Column.superclass.onRender.call(this, ct, position);
48327         if(this.width){
48328             this.el.setWidth(this.width);
48329         }
48330     }
48331 });
48332
48333
48334 /**
48335  * @class Roo.form.Row
48336  * @extends Roo.form.Layout
48337  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48338  * @constructor
48339  * @param {Object} config Configuration options
48340  */
48341
48342  
48343 Roo.form.Row = function(config){
48344     Roo.form.Row.superclass.constructor.call(this, config);
48345 };
48346  
48347 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48348       /**
48349      * @cfg {Number/String} width
48350      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48351      */
48352     /**
48353      * @cfg {Number/String} height
48354      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48355      */
48356     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48357     
48358     padWidth : 20,
48359     // private
48360     onRender : function(ct, position){
48361         //console.log('row render');
48362         if(!this.rowTpl){
48363             var t = new Roo.Template(
48364                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48365                     '<label for="{0}" style="{2}">{1}{4}</label>',
48366                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48367                     '</div>',
48368                 '</div>'
48369             );
48370             t.disableFormats = true;
48371             t.compile();
48372             Roo.form.Layout.prototype.rowTpl = t;
48373         }
48374         this.fieldTpl = this.rowTpl;
48375         
48376         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48377         var labelWidth = 100;
48378         
48379         if ((this.labelAlign != 'top')) {
48380             if (typeof this.labelWidth == 'number') {
48381                 labelWidth = this.labelWidth
48382             }
48383             this.padWidth =  20 + labelWidth;
48384             
48385         }
48386         
48387         Roo.form.Column.superclass.onRender.call(this, ct, position);
48388         if(this.width){
48389             this.el.setWidth(this.width);
48390         }
48391         if(this.height){
48392             this.el.setHeight(this.height);
48393         }
48394     },
48395     
48396     // private
48397     renderField : function(f){
48398         f.fieldEl = this.fieldTpl.append(this.el, [
48399                f.id, f.fieldLabel,
48400                f.labelStyle||this.labelStyle||'',
48401                this.elementStyle||'',
48402                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48403                f.itemCls||this.itemCls||'',
48404                f.width ? f.width + this.padWidth : 160 + this.padWidth
48405        ],true);
48406     }
48407 });
48408  
48409
48410 /**
48411  * @class Roo.form.FieldSet
48412  * @extends Roo.form.Layout
48413  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48414  * @constructor
48415  * @param {Object} config Configuration options
48416  */
48417 Roo.form.FieldSet = function(config){
48418     Roo.form.FieldSet.superclass.constructor.call(this, config);
48419 };
48420
48421 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48422     /**
48423      * @cfg {String} legend
48424      * The text to display as the legend for the FieldSet (defaults to '')
48425      */
48426     /**
48427      * @cfg {String/Object} autoCreate
48428      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48429      */
48430
48431     // private
48432     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48433
48434     // private
48435     onRender : function(ct, position){
48436         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48437         if(this.legend){
48438             this.setLegend(this.legend);
48439         }
48440     },
48441
48442     // private
48443     setLegend : function(text){
48444         if(this.rendered){
48445             this.el.child('legend').update(text);
48446         }
48447     }
48448 });/*
48449  * Based on:
48450  * Ext JS Library 1.1.1
48451  * Copyright(c) 2006-2007, Ext JS, LLC.
48452  *
48453  * Originally Released Under LGPL - original licence link has changed is not relivant.
48454  *
48455  * Fork - LGPL
48456  * <script type="text/javascript">
48457  */
48458 /**
48459  * @class Roo.form.VTypes
48460  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48461  * @singleton
48462  */
48463 Roo.form.VTypes = function(){
48464     // closure these in so they are only created once.
48465     var alpha = /^[a-zA-Z_]+$/;
48466     var alphanum = /^[a-zA-Z0-9_]+$/;
48467     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48468     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48469
48470     // All these messages and functions are configurable
48471     return {
48472         /**
48473          * The function used to validate email addresses
48474          * @param {String} value The email address
48475          */
48476         'email' : function(v){
48477             return email.test(v);
48478         },
48479         /**
48480          * The error text to display when the email validation function returns false
48481          * @type String
48482          */
48483         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48484         /**
48485          * The keystroke filter mask to be applied on email input
48486          * @type RegExp
48487          */
48488         'emailMask' : /[a-z0-9_\.\-@]/i,
48489
48490         /**
48491          * The function used to validate URLs
48492          * @param {String} value The URL
48493          */
48494         'url' : function(v){
48495             return url.test(v);
48496         },
48497         /**
48498          * The error text to display when the url validation function returns false
48499          * @type String
48500          */
48501         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48502         
48503         /**
48504          * The function used to validate alpha values
48505          * @param {String} value The value
48506          */
48507         'alpha' : function(v){
48508             return alpha.test(v);
48509         },
48510         /**
48511          * The error text to display when the alpha validation function returns false
48512          * @type String
48513          */
48514         'alphaText' : 'This field should only contain letters and _',
48515         /**
48516          * The keystroke filter mask to be applied on alpha input
48517          * @type RegExp
48518          */
48519         'alphaMask' : /[a-z_]/i,
48520
48521         /**
48522          * The function used to validate alphanumeric values
48523          * @param {String} value The value
48524          */
48525         'alphanum' : function(v){
48526             return alphanum.test(v);
48527         },
48528         /**
48529          * The error text to display when the alphanumeric validation function returns false
48530          * @type String
48531          */
48532         'alphanumText' : 'This field should only contain letters, numbers and _',
48533         /**
48534          * The keystroke filter mask to be applied on alphanumeric input
48535          * @type RegExp
48536          */
48537         'alphanumMask' : /[a-z0-9_]/i
48538     };
48539 }();//<script type="text/javascript">
48540
48541 /**
48542  * @class Roo.form.FCKeditor
48543  * @extends Roo.form.TextArea
48544  * Wrapper around the FCKEditor http://www.fckeditor.net
48545  * @constructor
48546  * Creates a new FCKeditor
48547  * @param {Object} config Configuration options
48548  */
48549 Roo.form.FCKeditor = function(config){
48550     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48551     this.addEvents({
48552          /**
48553          * @event editorinit
48554          * Fired when the editor is initialized - you can add extra handlers here..
48555          * @param {FCKeditor} this
48556          * @param {Object} the FCK object.
48557          */
48558         editorinit : true
48559     });
48560     
48561     
48562 };
48563 Roo.form.FCKeditor.editors = { };
48564 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48565 {
48566     //defaultAutoCreate : {
48567     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48568     //},
48569     // private
48570     /**
48571      * @cfg {Object} fck options - see fck manual for details.
48572      */
48573     fckconfig : false,
48574     
48575     /**
48576      * @cfg {Object} fck toolbar set (Basic or Default)
48577      */
48578     toolbarSet : 'Basic',
48579     /**
48580      * @cfg {Object} fck BasePath
48581      */ 
48582     basePath : '/fckeditor/',
48583     
48584     
48585     frame : false,
48586     
48587     value : '',
48588     
48589    
48590     onRender : function(ct, position)
48591     {
48592         if(!this.el){
48593             this.defaultAutoCreate = {
48594                 tag: "textarea",
48595                 style:"width:300px;height:60px;",
48596                 autocomplete: "new-password"
48597             };
48598         }
48599         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48600         /*
48601         if(this.grow){
48602             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48603             if(this.preventScrollbars){
48604                 this.el.setStyle("overflow", "hidden");
48605             }
48606             this.el.setHeight(this.growMin);
48607         }
48608         */
48609         //console.log('onrender' + this.getId() );
48610         Roo.form.FCKeditor.editors[this.getId()] = this;
48611          
48612
48613         this.replaceTextarea() ;
48614         
48615     },
48616     
48617     getEditor : function() {
48618         return this.fckEditor;
48619     },
48620     /**
48621      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48622      * @param {Mixed} value The value to set
48623      */
48624     
48625     
48626     setValue : function(value)
48627     {
48628         //console.log('setValue: ' + value);
48629         
48630         if(typeof(value) == 'undefined') { // not sure why this is happending...
48631             return;
48632         }
48633         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48634         
48635         //if(!this.el || !this.getEditor()) {
48636         //    this.value = value;
48637             //this.setValue.defer(100,this,[value]);    
48638         //    return;
48639         //} 
48640         
48641         if(!this.getEditor()) {
48642             return;
48643         }
48644         
48645         this.getEditor().SetData(value);
48646         
48647         //
48648
48649     },
48650
48651     /**
48652      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
48653      * @return {Mixed} value The field value
48654      */
48655     getValue : function()
48656     {
48657         
48658         if (this.frame && this.frame.dom.style.display == 'none') {
48659             return Roo.form.FCKeditor.superclass.getValue.call(this);
48660         }
48661         
48662         if(!this.el || !this.getEditor()) {
48663            
48664            // this.getValue.defer(100,this); 
48665             return this.value;
48666         }
48667        
48668         
48669         var value=this.getEditor().GetData();
48670         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48671         return Roo.form.FCKeditor.superclass.getValue.call(this);
48672         
48673
48674     },
48675
48676     /**
48677      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
48678      * @return {Mixed} value The field value
48679      */
48680     getRawValue : function()
48681     {
48682         if (this.frame && this.frame.dom.style.display == 'none') {
48683             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48684         }
48685         
48686         if(!this.el || !this.getEditor()) {
48687             //this.getRawValue.defer(100,this); 
48688             return this.value;
48689             return;
48690         }
48691         
48692         
48693         
48694         var value=this.getEditor().GetData();
48695         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
48696         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48697          
48698     },
48699     
48700     setSize : function(w,h) {
48701         
48702         
48703         
48704         //if (this.frame && this.frame.dom.style.display == 'none') {
48705         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48706         //    return;
48707         //}
48708         //if(!this.el || !this.getEditor()) {
48709         //    this.setSize.defer(100,this, [w,h]); 
48710         //    return;
48711         //}
48712         
48713         
48714         
48715         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48716         
48717         this.frame.dom.setAttribute('width', w);
48718         this.frame.dom.setAttribute('height', h);
48719         this.frame.setSize(w,h);
48720         
48721     },
48722     
48723     toggleSourceEdit : function(value) {
48724         
48725       
48726          
48727         this.el.dom.style.display = value ? '' : 'none';
48728         this.frame.dom.style.display = value ?  'none' : '';
48729         
48730     },
48731     
48732     
48733     focus: function(tag)
48734     {
48735         if (this.frame.dom.style.display == 'none') {
48736             return Roo.form.FCKeditor.superclass.focus.call(this);
48737         }
48738         if(!this.el || !this.getEditor()) {
48739             this.focus.defer(100,this, [tag]); 
48740             return;
48741         }
48742         
48743         
48744         
48745         
48746         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
48747         this.getEditor().Focus();
48748         if (tgs.length) {
48749             if (!this.getEditor().Selection.GetSelection()) {
48750                 this.focus.defer(100,this, [tag]); 
48751                 return;
48752             }
48753             
48754             
48755             var r = this.getEditor().EditorDocument.createRange();
48756             r.setStart(tgs[0],0);
48757             r.setEnd(tgs[0],0);
48758             this.getEditor().Selection.GetSelection().removeAllRanges();
48759             this.getEditor().Selection.GetSelection().addRange(r);
48760             this.getEditor().Focus();
48761         }
48762         
48763     },
48764     
48765     
48766     
48767     replaceTextarea : function()
48768     {
48769         if ( document.getElementById( this.getId() + '___Frame' ) ) {
48770             return ;
48771         }
48772         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
48773         //{
48774             // We must check the elements firstly using the Id and then the name.
48775         var oTextarea = document.getElementById( this.getId() );
48776         
48777         var colElementsByName = document.getElementsByName( this.getId() ) ;
48778          
48779         oTextarea.style.display = 'none' ;
48780
48781         if ( oTextarea.tabIndex ) {            
48782             this.TabIndex = oTextarea.tabIndex ;
48783         }
48784         
48785         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
48786         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
48787         this.frame = Roo.get(this.getId() + '___Frame')
48788     },
48789     
48790     _getConfigHtml : function()
48791     {
48792         var sConfig = '' ;
48793
48794         for ( var o in this.fckconfig ) {
48795             sConfig += sConfig.length > 0  ? '&amp;' : '';
48796             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
48797         }
48798
48799         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
48800     },
48801     
48802     
48803     _getIFrameHtml : function()
48804     {
48805         var sFile = 'fckeditor.html' ;
48806         /* no idea what this is about..
48807         try
48808         {
48809             if ( (/fcksource=true/i).test( window.top.location.search ) )
48810                 sFile = 'fckeditor.original.html' ;
48811         }
48812         catch (e) { 
48813         */
48814
48815         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
48816         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
48817         
48818         
48819         var html = '<iframe id="' + this.getId() +
48820             '___Frame" src="' + sLink +
48821             '" width="' + this.width +
48822             '" height="' + this.height + '"' +
48823             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
48824             ' frameborder="0" scrolling="no"></iframe>' ;
48825
48826         return html ;
48827     },
48828     
48829     _insertHtmlBefore : function( html, element )
48830     {
48831         if ( element.insertAdjacentHTML )       {
48832             // IE
48833             element.insertAdjacentHTML( 'beforeBegin', html ) ;
48834         } else { // Gecko
48835             var oRange = document.createRange() ;
48836             oRange.setStartBefore( element ) ;
48837             var oFragment = oRange.createContextualFragment( html );
48838             element.parentNode.insertBefore( oFragment, element ) ;
48839         }
48840     }
48841     
48842     
48843   
48844     
48845     
48846     
48847     
48848
48849 });
48850
48851 //Roo.reg('fckeditor', Roo.form.FCKeditor);
48852
48853 function FCKeditor_OnComplete(editorInstance){
48854     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
48855     f.fckEditor = editorInstance;
48856     //console.log("loaded");
48857     f.fireEvent('editorinit', f, editorInstance);
48858
48859   
48860
48861  
48862
48863
48864
48865
48866
48867
48868
48869
48870
48871
48872
48873
48874
48875
48876
48877 //<script type="text/javascript">
48878 /**
48879  * @class Roo.form.GridField
48880  * @extends Roo.form.Field
48881  * Embed a grid (or editable grid into a form)
48882  * STATUS ALPHA
48883  * 
48884  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
48885  * it needs 
48886  * xgrid.store = Roo.data.Store
48887  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
48888  * xgrid.store.reader = Roo.data.JsonReader 
48889  * 
48890  * 
48891  * @constructor
48892  * Creates a new GridField
48893  * @param {Object} config Configuration options
48894  */
48895 Roo.form.GridField = function(config){
48896     Roo.form.GridField.superclass.constructor.call(this, config);
48897      
48898 };
48899
48900 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
48901     /**
48902      * @cfg {Number} width  - used to restrict width of grid..
48903      */
48904     width : 100,
48905     /**
48906      * @cfg {Number} height - used to restrict height of grid..
48907      */
48908     height : 50,
48909      /**
48910      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
48911          * 
48912          *}
48913      */
48914     xgrid : false, 
48915     /**
48916      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48917      * {tag: "input", type: "checkbox", autocomplete: "off"})
48918      */
48919    // defaultAutoCreate : { tag: 'div' },
48920     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
48921     /**
48922      * @cfg {String} addTitle Text to include for adding a title.
48923      */
48924     addTitle : false,
48925     //
48926     onResize : function(){
48927         Roo.form.Field.superclass.onResize.apply(this, arguments);
48928     },
48929
48930     initEvents : function(){
48931         // Roo.form.Checkbox.superclass.initEvents.call(this);
48932         // has no events...
48933        
48934     },
48935
48936
48937     getResizeEl : function(){
48938         return this.wrap;
48939     },
48940
48941     getPositionEl : function(){
48942         return this.wrap;
48943     },
48944
48945     // private
48946     onRender : function(ct, position){
48947         
48948         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
48949         var style = this.style;
48950         delete this.style;
48951         
48952         Roo.form.GridField.superclass.onRender.call(this, ct, position);
48953         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
48954         this.viewEl = this.wrap.createChild({ tag: 'div' });
48955         if (style) {
48956             this.viewEl.applyStyles(style);
48957         }
48958         if (this.width) {
48959             this.viewEl.setWidth(this.width);
48960         }
48961         if (this.height) {
48962             this.viewEl.setHeight(this.height);
48963         }
48964         //if(this.inputValue !== undefined){
48965         //this.setValue(this.value);
48966         
48967         
48968         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
48969         
48970         
48971         this.grid.render();
48972         this.grid.getDataSource().on('remove', this.refreshValue, this);
48973         this.grid.getDataSource().on('update', this.refreshValue, this);
48974         this.grid.on('afteredit', this.refreshValue, this);
48975  
48976     },
48977      
48978     
48979     /**
48980      * Sets the value of the item. 
48981      * @param {String} either an object  or a string..
48982      */
48983     setValue : function(v){
48984         //this.value = v;
48985         v = v || []; // empty set..
48986         // this does not seem smart - it really only affects memoryproxy grids..
48987         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
48988             var ds = this.grid.getDataSource();
48989             // assumes a json reader..
48990             var data = {}
48991             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
48992             ds.loadData( data);
48993         }
48994         // clear selection so it does not get stale.
48995         if (this.grid.sm) { 
48996             this.grid.sm.clearSelections();
48997         }
48998         
48999         Roo.form.GridField.superclass.setValue.call(this, v);
49000         this.refreshValue();
49001         // should load data in the grid really....
49002     },
49003     
49004     // private
49005     refreshValue: function() {
49006          var val = [];
49007         this.grid.getDataSource().each(function(r) {
49008             val.push(r.data);
49009         });
49010         this.el.dom.value = Roo.encode(val);
49011     }
49012     
49013      
49014     
49015     
49016 });/*
49017  * Based on:
49018  * Ext JS Library 1.1.1
49019  * Copyright(c) 2006-2007, Ext JS, LLC.
49020  *
49021  * Originally Released Under LGPL - original licence link has changed is not relivant.
49022  *
49023  * Fork - LGPL
49024  * <script type="text/javascript">
49025  */
49026 /**
49027  * @class Roo.form.DisplayField
49028  * @extends Roo.form.Field
49029  * A generic Field to display non-editable data.
49030  * @cfg {Boolean} closable (true|false) default false
49031  * @constructor
49032  * Creates a new Display Field item.
49033  * @param {Object} config Configuration options
49034  */
49035 Roo.form.DisplayField = function(config){
49036     Roo.form.DisplayField.superclass.constructor.call(this, config);
49037     
49038     this.addEvents({
49039         /**
49040          * @event close
49041          * Fires after the click the close btn
49042              * @param {Roo.form.DisplayField} this
49043              */
49044         close : true
49045     });
49046 };
49047
49048 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
49049     inputType:      'hidden',
49050     allowBlank:     true,
49051     readOnly:         true,
49052     
49053  
49054     /**
49055      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49056      */
49057     focusClass : undefined,
49058     /**
49059      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49060      */
49061     fieldClass: 'x-form-field',
49062     
49063      /**
49064      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
49065      */
49066     valueRenderer: undefined,
49067     
49068     width: 100,
49069     /**
49070      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49071      * {tag: "input", type: "checkbox", autocomplete: "off"})
49072      */
49073      
49074  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
49075  
49076     closable : false,
49077     
49078     onResize : function(){
49079         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
49080         
49081     },
49082
49083     initEvents : function(){
49084         // Roo.form.Checkbox.superclass.initEvents.call(this);
49085         // has no events...
49086         
49087         if(this.closable){
49088             this.closeEl.on('click', this.onClose, this);
49089         }
49090        
49091     },
49092
49093
49094     getResizeEl : function(){
49095         return this.wrap;
49096     },
49097
49098     getPositionEl : function(){
49099         return this.wrap;
49100     },
49101
49102     // private
49103     onRender : function(ct, position){
49104         
49105         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
49106         //if(this.inputValue !== undefined){
49107         this.wrap = this.el.wrap();
49108         
49109         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
49110         
49111         if(this.closable){
49112             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
49113         }
49114         
49115         if (this.bodyStyle) {
49116             this.viewEl.applyStyles(this.bodyStyle);
49117         }
49118         //this.viewEl.setStyle('padding', '2px');
49119         
49120         this.setValue(this.value);
49121         
49122     },
49123 /*
49124     // private
49125     initValue : Roo.emptyFn,
49126
49127   */
49128
49129         // private
49130     onClick : function(){
49131         
49132     },
49133
49134     /**
49135      * Sets the checked state of the checkbox.
49136      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
49137      */
49138     setValue : function(v){
49139         this.value = v;
49140         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
49141         // this might be called before we have a dom element..
49142         if (!this.viewEl) {
49143             return;
49144         }
49145         this.viewEl.dom.innerHTML = html;
49146         Roo.form.DisplayField.superclass.setValue.call(this, v);
49147
49148     },
49149     
49150     onClose : function(e)
49151     {
49152         e.preventDefault();
49153         
49154         this.fireEvent('close', this);
49155     }
49156 });/*
49157  * 
49158  * Licence- LGPL
49159  * 
49160  */
49161
49162 /**
49163  * @class Roo.form.DayPicker
49164  * @extends Roo.form.Field
49165  * A Day picker show [M] [T] [W] ....
49166  * @constructor
49167  * Creates a new Day Picker
49168  * @param {Object} config Configuration options
49169  */
49170 Roo.form.DayPicker= function(config){
49171     Roo.form.DayPicker.superclass.constructor.call(this, config);
49172      
49173 };
49174
49175 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49176     /**
49177      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49178      */
49179     focusClass : undefined,
49180     /**
49181      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49182      */
49183     fieldClass: "x-form-field",
49184    
49185     /**
49186      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49187      * {tag: "input", type: "checkbox", autocomplete: "off"})
49188      */
49189     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49190     
49191    
49192     actionMode : 'viewEl', 
49193     //
49194     // private
49195  
49196     inputType : 'hidden',
49197     
49198      
49199     inputElement: false, // real input element?
49200     basedOn: false, // ????
49201     
49202     isFormField: true, // not sure where this is needed!!!!
49203
49204     onResize : function(){
49205         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49206         if(!this.boxLabel){
49207             this.el.alignTo(this.wrap, 'c-c');
49208         }
49209     },
49210
49211     initEvents : function(){
49212         Roo.form.Checkbox.superclass.initEvents.call(this);
49213         this.el.on("click", this.onClick,  this);
49214         this.el.on("change", this.onClick,  this);
49215     },
49216
49217
49218     getResizeEl : function(){
49219         return this.wrap;
49220     },
49221
49222     getPositionEl : function(){
49223         return this.wrap;
49224     },
49225
49226     
49227     // private
49228     onRender : function(ct, position){
49229         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49230        
49231         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49232         
49233         var r1 = '<table><tr>';
49234         var r2 = '<tr class="x-form-daypick-icons">';
49235         for (var i=0; i < 7; i++) {
49236             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49237             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49238         }
49239         
49240         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49241         viewEl.select('img').on('click', this.onClick, this);
49242         this.viewEl = viewEl;   
49243         
49244         
49245         // this will not work on Chrome!!!
49246         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49247         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49248         
49249         
49250           
49251
49252     },
49253
49254     // private
49255     initValue : Roo.emptyFn,
49256
49257     /**
49258      * Returns the checked state of the checkbox.
49259      * @return {Boolean} True if checked, else false
49260      */
49261     getValue : function(){
49262         return this.el.dom.value;
49263         
49264     },
49265
49266         // private
49267     onClick : function(e){ 
49268         //this.setChecked(!this.checked);
49269         Roo.get(e.target).toggleClass('x-menu-item-checked');
49270         this.refreshValue();
49271         //if(this.el.dom.checked != this.checked){
49272         //    this.setValue(this.el.dom.checked);
49273        // }
49274     },
49275     
49276     // private
49277     refreshValue : function()
49278     {
49279         var val = '';
49280         this.viewEl.select('img',true).each(function(e,i,n)  {
49281             val += e.is(".x-menu-item-checked") ? String(n) : '';
49282         });
49283         this.setValue(val, true);
49284     },
49285
49286     /**
49287      * Sets the checked state of the checkbox.
49288      * On is always based on a string comparison between inputValue and the param.
49289      * @param {Boolean/String} value - the value to set 
49290      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49291      */
49292     setValue : function(v,suppressEvent){
49293         if (!this.el.dom) {
49294             return;
49295         }
49296         var old = this.el.dom.value ;
49297         this.el.dom.value = v;
49298         if (suppressEvent) {
49299             return ;
49300         }
49301          
49302         // update display..
49303         this.viewEl.select('img',true).each(function(e,i,n)  {
49304             
49305             var on = e.is(".x-menu-item-checked");
49306             var newv = v.indexOf(String(n)) > -1;
49307             if (on != newv) {
49308                 e.toggleClass('x-menu-item-checked');
49309             }
49310             
49311         });
49312         
49313         
49314         this.fireEvent('change', this, v, old);
49315         
49316         
49317     },
49318    
49319     // handle setting of hidden value by some other method!!?!?
49320     setFromHidden: function()
49321     {
49322         if(!this.el){
49323             return;
49324         }
49325         //console.log("SET FROM HIDDEN");
49326         //alert('setFrom hidden');
49327         this.setValue(this.el.dom.value);
49328     },
49329     
49330     onDestroy : function()
49331     {
49332         if(this.viewEl){
49333             Roo.get(this.viewEl).remove();
49334         }
49335          
49336         Roo.form.DayPicker.superclass.onDestroy.call(this);
49337     }
49338
49339 });/*
49340  * RooJS Library 1.1.1
49341  * Copyright(c) 2008-2011  Alan Knowles
49342  *
49343  * License - LGPL
49344  */
49345  
49346
49347 /**
49348  * @class Roo.form.ComboCheck
49349  * @extends Roo.form.ComboBox
49350  * A combobox for multiple select items.
49351  *
49352  * FIXME - could do with a reset button..
49353  * 
49354  * @constructor
49355  * Create a new ComboCheck
49356  * @param {Object} config Configuration options
49357  */
49358 Roo.form.ComboCheck = function(config){
49359     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49360     // should verify some data...
49361     // like
49362     // hiddenName = required..
49363     // displayField = required
49364     // valudField == required
49365     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49366     var _t = this;
49367     Roo.each(req, function(e) {
49368         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49369             throw "Roo.form.ComboCheck : missing value for: " + e;
49370         }
49371     });
49372     
49373     
49374 };
49375
49376 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49377      
49378      
49379     editable : false,
49380      
49381     selectedClass: 'x-menu-item-checked', 
49382     
49383     // private
49384     onRender : function(ct, position){
49385         var _t = this;
49386         
49387         
49388         
49389         if(!this.tpl){
49390             var cls = 'x-combo-list';
49391
49392             
49393             this.tpl =  new Roo.Template({
49394                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49395                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49396                    '<span>{' + this.displayField + '}</span>' +
49397                     '</div>' 
49398                 
49399             });
49400         }
49401  
49402         
49403         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49404         this.view.singleSelect = false;
49405         this.view.multiSelect = true;
49406         this.view.toggleSelect = true;
49407         this.pageTb.add(new Roo.Toolbar.Fill(), {
49408             
49409             text: 'Done',
49410             handler: function()
49411             {
49412                 _t.collapse();
49413             }
49414         });
49415     },
49416     
49417     onViewOver : function(e, t){
49418         // do nothing...
49419         return;
49420         
49421     },
49422     
49423     onViewClick : function(doFocus,index){
49424         return;
49425         
49426     },
49427     select: function () {
49428         //Roo.log("SELECT CALLED");
49429     },
49430      
49431     selectByValue : function(xv, scrollIntoView){
49432         var ar = this.getValueArray();
49433         var sels = [];
49434         
49435         Roo.each(ar, function(v) {
49436             if(v === undefined || v === null){
49437                 return;
49438             }
49439             var r = this.findRecord(this.valueField, v);
49440             if(r){
49441                 sels.push(this.store.indexOf(r))
49442                 
49443             }
49444         },this);
49445         this.view.select(sels);
49446         return false;
49447     },
49448     
49449     
49450     
49451     onSelect : function(record, index){
49452        // Roo.log("onselect Called");
49453        // this is only called by the clear button now..
49454         this.view.clearSelections();
49455         this.setValue('[]');
49456         if (this.value != this.valueBefore) {
49457             this.fireEvent('change', this, this.value, this.valueBefore);
49458             this.valueBefore = this.value;
49459         }
49460     },
49461     getValueArray : function()
49462     {
49463         var ar = [] ;
49464         
49465         try {
49466             //Roo.log(this.value);
49467             if (typeof(this.value) == 'undefined') {
49468                 return [];
49469             }
49470             var ar = Roo.decode(this.value);
49471             return  ar instanceof Array ? ar : []; //?? valid?
49472             
49473         } catch(e) {
49474             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49475             return [];
49476         }
49477          
49478     },
49479     expand : function ()
49480     {
49481         
49482         Roo.form.ComboCheck.superclass.expand.call(this);
49483         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49484         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49485         
49486
49487     },
49488     
49489     collapse : function(){
49490         Roo.form.ComboCheck.superclass.collapse.call(this);
49491         var sl = this.view.getSelectedIndexes();
49492         var st = this.store;
49493         var nv = [];
49494         var tv = [];
49495         var r;
49496         Roo.each(sl, function(i) {
49497             r = st.getAt(i);
49498             nv.push(r.get(this.valueField));
49499         },this);
49500         this.setValue(Roo.encode(nv));
49501         if (this.value != this.valueBefore) {
49502
49503             this.fireEvent('change', this, this.value, this.valueBefore);
49504             this.valueBefore = this.value;
49505         }
49506         
49507     },
49508     
49509     setValue : function(v){
49510         // Roo.log(v);
49511         this.value = v;
49512         
49513         var vals = this.getValueArray();
49514         var tv = [];
49515         Roo.each(vals, function(k) {
49516             var r = this.findRecord(this.valueField, k);
49517             if(r){
49518                 tv.push(r.data[this.displayField]);
49519             }else if(this.valueNotFoundText !== undefined){
49520                 tv.push( this.valueNotFoundText );
49521             }
49522         },this);
49523        // Roo.log(tv);
49524         
49525         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49526         this.hiddenField.value = v;
49527         this.value = v;
49528     }
49529     
49530 });/*
49531  * Based on:
49532  * Ext JS Library 1.1.1
49533  * Copyright(c) 2006-2007, Ext JS, LLC.
49534  *
49535  * Originally Released Under LGPL - original licence link has changed is not relivant.
49536  *
49537  * Fork - LGPL
49538  * <script type="text/javascript">
49539  */
49540  
49541 /**
49542  * @class Roo.form.Signature
49543  * @extends Roo.form.Field
49544  * Signature field.  
49545  * @constructor
49546  * 
49547  * @param {Object} config Configuration options
49548  */
49549
49550 Roo.form.Signature = function(config){
49551     Roo.form.Signature.superclass.constructor.call(this, config);
49552     
49553     this.addEvents({// not in used??
49554          /**
49555          * @event confirm
49556          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49557              * @param {Roo.form.Signature} combo This combo box
49558              */
49559         'confirm' : true,
49560         /**
49561          * @event reset
49562          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49563              * @param {Roo.form.ComboBox} combo This combo box
49564              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49565              */
49566         'reset' : true
49567     });
49568 };
49569
49570 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49571     /**
49572      * @cfg {Object} labels Label to use when rendering a form.
49573      * defaults to 
49574      * labels : { 
49575      *      clear : "Clear",
49576      *      confirm : "Confirm"
49577      *  }
49578      */
49579     labels : { 
49580         clear : "Clear",
49581         confirm : "Confirm"
49582     },
49583     /**
49584      * @cfg {Number} width The signature panel width (defaults to 300)
49585      */
49586     width: 300,
49587     /**
49588      * @cfg {Number} height The signature panel height (defaults to 100)
49589      */
49590     height : 100,
49591     /**
49592      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49593      */
49594     allowBlank : false,
49595     
49596     //private
49597     // {Object} signPanel The signature SVG panel element (defaults to {})
49598     signPanel : {},
49599     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49600     isMouseDown : false,
49601     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49602     isConfirmed : false,
49603     // {String} signatureTmp SVG mapping string (defaults to empty string)
49604     signatureTmp : '',
49605     
49606     
49607     defaultAutoCreate : { // modified by initCompnoent..
49608         tag: "input",
49609         type:"hidden"
49610     },
49611
49612     // private
49613     onRender : function(ct, position){
49614         
49615         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49616         
49617         this.wrap = this.el.wrap({
49618             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49619         });
49620         
49621         this.createToolbar(this);
49622         this.signPanel = this.wrap.createChild({
49623                 tag: 'div',
49624                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49625             }, this.el
49626         );
49627             
49628         this.svgID = Roo.id();
49629         this.svgEl = this.signPanel.createChild({
49630               xmlns : 'http://www.w3.org/2000/svg',
49631               tag : 'svg',
49632               id : this.svgID + "-svg",
49633               width: this.width,
49634               height: this.height,
49635               viewBox: '0 0 '+this.width+' '+this.height,
49636               cn : [
49637                 {
49638                     tag: "rect",
49639                     id: this.svgID + "-svg-r",
49640                     width: this.width,
49641                     height: this.height,
49642                     fill: "#ffa"
49643                 },
49644                 {
49645                     tag: "line",
49646                     id: this.svgID + "-svg-l",
49647                     x1: "0", // start
49648                     y1: (this.height*0.8), // start set the line in 80% of height
49649                     x2: this.width, // end
49650                     y2: (this.height*0.8), // end set the line in 80% of height
49651                     'stroke': "#666",
49652                     'stroke-width': "1",
49653                     'stroke-dasharray': "3",
49654                     'shape-rendering': "crispEdges",
49655                     'pointer-events': "none"
49656                 },
49657                 {
49658                     tag: "path",
49659                     id: this.svgID + "-svg-p",
49660                     'stroke': "navy",
49661                     'stroke-width': "3",
49662                     'fill': "none",
49663                     'pointer-events': 'none'
49664                 }
49665               ]
49666         });
49667         this.createSVG();
49668         this.svgBox = this.svgEl.dom.getScreenCTM();
49669     },
49670     createSVG : function(){ 
49671         var svg = this.signPanel;
49672         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
49673         var t = this;
49674
49675         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
49676         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
49677         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
49678         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
49679         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
49680         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
49681         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
49682         
49683     },
49684     isTouchEvent : function(e){
49685         return e.type.match(/^touch/);
49686     },
49687     getCoords : function (e) {
49688         var pt    = this.svgEl.dom.createSVGPoint();
49689         pt.x = e.clientX; 
49690         pt.y = e.clientY;
49691         if (this.isTouchEvent(e)) {
49692             pt.x =  e.targetTouches[0].clientX;
49693             pt.y = e.targetTouches[0].clientY;
49694         }
49695         var a = this.svgEl.dom.getScreenCTM();
49696         var b = a.inverse();
49697         var mx = pt.matrixTransform(b);
49698         return mx.x + ',' + mx.y;
49699     },
49700     //mouse event headler 
49701     down : function (e) {
49702         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
49703         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
49704         
49705         this.isMouseDown = true;
49706         
49707         e.preventDefault();
49708     },
49709     move : function (e) {
49710         if (this.isMouseDown) {
49711             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
49712             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
49713         }
49714         
49715         e.preventDefault();
49716     },
49717     up : function (e) {
49718         this.isMouseDown = false;
49719         var sp = this.signatureTmp.split(' ');
49720         
49721         if(sp.length > 1){
49722             if(!sp[sp.length-2].match(/^L/)){
49723                 sp.pop();
49724                 sp.pop();
49725                 sp.push("");
49726                 this.signatureTmp = sp.join(" ");
49727             }
49728         }
49729         if(this.getValue() != this.signatureTmp){
49730             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49731             this.isConfirmed = false;
49732         }
49733         e.preventDefault();
49734     },
49735     
49736     /**
49737      * Protected method that will not generally be called directly. It
49738      * is called when the editor creates its toolbar. Override this method if you need to
49739      * add custom toolbar buttons.
49740      * @param {HtmlEditor} editor
49741      */
49742     createToolbar : function(editor){
49743          function btn(id, toggle, handler){
49744             var xid = fid + '-'+ id ;
49745             return {
49746                 id : xid,
49747                 cmd : id,
49748                 cls : 'x-btn-icon x-edit-'+id,
49749                 enableToggle:toggle !== false,
49750                 scope: editor, // was editor...
49751                 handler:handler||editor.relayBtnCmd,
49752                 clickEvent:'mousedown',
49753                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49754                 tabIndex:-1
49755             };
49756         }
49757         
49758         
49759         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49760         this.tb = tb;
49761         this.tb.add(
49762            {
49763                 cls : ' x-signature-btn x-signature-'+id,
49764                 scope: editor, // was editor...
49765                 handler: this.reset,
49766                 clickEvent:'mousedown',
49767                 text: this.labels.clear
49768             },
49769             {
49770                  xtype : 'Fill',
49771                  xns: Roo.Toolbar
49772             }, 
49773             {
49774                 cls : '  x-signature-btn x-signature-'+id,
49775                 scope: editor, // was editor...
49776                 handler: this.confirmHandler,
49777                 clickEvent:'mousedown',
49778                 text: this.labels.confirm
49779             }
49780         );
49781     
49782     },
49783     //public
49784     /**
49785      * when user is clicked confirm then show this image.....
49786      * 
49787      * @return {String} Image Data URI
49788      */
49789     getImageDataURI : function(){
49790         var svg = this.svgEl.dom.parentNode.innerHTML;
49791         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
49792         return src; 
49793     },
49794     /**
49795      * 
49796      * @return {Boolean} this.isConfirmed
49797      */
49798     getConfirmed : function(){
49799         return this.isConfirmed;
49800     },
49801     /**
49802      * 
49803      * @return {Number} this.width
49804      */
49805     getWidth : function(){
49806         return this.width;
49807     },
49808     /**
49809      * 
49810      * @return {Number} this.height
49811      */
49812     getHeight : function(){
49813         return this.height;
49814     },
49815     // private
49816     getSignature : function(){
49817         return this.signatureTmp;
49818     },
49819     // private
49820     reset : function(){
49821         this.signatureTmp = '';
49822         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49823         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
49824         this.isConfirmed = false;
49825         Roo.form.Signature.superclass.reset.call(this);
49826     },
49827     setSignature : function(s){
49828         this.signatureTmp = s;
49829         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49830         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
49831         this.setValue(s);
49832         this.isConfirmed = false;
49833         Roo.form.Signature.superclass.reset.call(this);
49834     }, 
49835     test : function(){
49836 //        Roo.log(this.signPanel.dom.contentWindow.up())
49837     },
49838     //private
49839     setConfirmed : function(){
49840         
49841         
49842         
49843 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
49844     },
49845     // private
49846     confirmHandler : function(){
49847         if(!this.getSignature()){
49848             return;
49849         }
49850         
49851         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
49852         this.setValue(this.getSignature());
49853         this.isConfirmed = true;
49854         
49855         this.fireEvent('confirm', this);
49856     },
49857     // private
49858     // Subclasses should provide the validation implementation by overriding this
49859     validateValue : function(value){
49860         if(this.allowBlank){
49861             return true;
49862         }
49863         
49864         if(this.isConfirmed){
49865             return true;
49866         }
49867         return false;
49868     }
49869 });/*
49870  * Based on:
49871  * Ext JS Library 1.1.1
49872  * Copyright(c) 2006-2007, Ext JS, LLC.
49873  *
49874  * Originally Released Under LGPL - original licence link has changed is not relivant.
49875  *
49876  * Fork - LGPL
49877  * <script type="text/javascript">
49878  */
49879  
49880
49881 /**
49882  * @class Roo.form.ComboBox
49883  * @extends Roo.form.TriggerField
49884  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
49885  * @constructor
49886  * Create a new ComboBox.
49887  * @param {Object} config Configuration options
49888  */
49889 Roo.form.Select = function(config){
49890     Roo.form.Select.superclass.constructor.call(this, config);
49891      
49892 };
49893
49894 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
49895     /**
49896      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
49897      */
49898     /**
49899      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
49900      * rendering into an Roo.Editor, defaults to false)
49901      */
49902     /**
49903      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
49904      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
49905      */
49906     /**
49907      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
49908      */
49909     /**
49910      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
49911      * the dropdown list (defaults to undefined, with no header element)
49912      */
49913
49914      /**
49915      * @cfg {String/Roo.Template} tpl The template to use to render the output
49916      */
49917      
49918     // private
49919     defaultAutoCreate : {tag: "select"  },
49920     /**
49921      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
49922      */
49923     listWidth: undefined,
49924     /**
49925      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
49926      * mode = 'remote' or 'text' if mode = 'local')
49927      */
49928     displayField: undefined,
49929     /**
49930      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
49931      * mode = 'remote' or 'value' if mode = 'local'). 
49932      * Note: use of a valueField requires the user make a selection
49933      * in order for a value to be mapped.
49934      */
49935     valueField: undefined,
49936     
49937     
49938     /**
49939      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
49940      * field's data value (defaults to the underlying DOM element's name)
49941      */
49942     hiddenName: undefined,
49943     /**
49944      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
49945      */
49946     listClass: '',
49947     /**
49948      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
49949      */
49950     selectedClass: 'x-combo-selected',
49951     /**
49952      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
49953      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
49954      * which displays a downward arrow icon).
49955      */
49956     triggerClass : 'x-form-arrow-trigger',
49957     /**
49958      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
49959      */
49960     shadow:'sides',
49961     /**
49962      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
49963      * anchor positions (defaults to 'tl-bl')
49964      */
49965     listAlign: 'tl-bl?',
49966     /**
49967      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
49968      */
49969     maxHeight: 300,
49970     /**
49971      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
49972      * query specified by the allQuery config option (defaults to 'query')
49973      */
49974     triggerAction: 'query',
49975     /**
49976      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
49977      * (defaults to 4, does not apply if editable = false)
49978      */
49979     minChars : 4,
49980     /**
49981      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
49982      * delay (typeAheadDelay) if it matches a known value (defaults to false)
49983      */
49984     typeAhead: false,
49985     /**
49986      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
49987      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
49988      */
49989     queryDelay: 500,
49990     /**
49991      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
49992      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
49993      */
49994     pageSize: 0,
49995     /**
49996      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
49997      * when editable = true (defaults to false)
49998      */
49999     selectOnFocus:false,
50000     /**
50001      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
50002      */
50003     queryParam: 'query',
50004     /**
50005      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
50006      * when mode = 'remote' (defaults to 'Loading...')
50007      */
50008     loadingText: 'Loading...',
50009     /**
50010      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
50011      */
50012     resizable: false,
50013     /**
50014      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
50015      */
50016     handleHeight : 8,
50017     /**
50018      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
50019      * traditional select (defaults to true)
50020      */
50021     editable: true,
50022     /**
50023      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
50024      */
50025     allQuery: '',
50026     /**
50027      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
50028      */
50029     mode: 'remote',
50030     /**
50031      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
50032      * listWidth has a higher value)
50033      */
50034     minListWidth : 70,
50035     /**
50036      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
50037      * allow the user to set arbitrary text into the field (defaults to false)
50038      */
50039     forceSelection:false,
50040     /**
50041      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
50042      * if typeAhead = true (defaults to 250)
50043      */
50044     typeAheadDelay : 250,
50045     /**
50046      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
50047      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
50048      */
50049     valueNotFoundText : undefined,
50050     
50051     /**
50052      * @cfg {String} defaultValue The value displayed after loading the store.
50053      */
50054     defaultValue: '',
50055     
50056     /**
50057      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
50058      */
50059     blockFocus : false,
50060     
50061     /**
50062      * @cfg {Boolean} disableClear Disable showing of clear button.
50063      */
50064     disableClear : false,
50065     /**
50066      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
50067      */
50068     alwaysQuery : false,
50069     
50070     //private
50071     addicon : false,
50072     editicon: false,
50073     
50074     // element that contains real text value.. (when hidden is used..)
50075      
50076     // private
50077     onRender : function(ct, position){
50078         Roo.form.Field.prototype.onRender.call(this, ct, position);
50079         
50080         if(this.store){
50081             this.store.on('beforeload', this.onBeforeLoad, this);
50082             this.store.on('load', this.onLoad, this);
50083             this.store.on('loadexception', this.onLoadException, this);
50084             this.store.load({});
50085         }
50086         
50087         
50088         
50089     },
50090
50091     // private
50092     initEvents : function(){
50093         //Roo.form.ComboBox.superclass.initEvents.call(this);
50094  
50095     },
50096
50097     onDestroy : function(){
50098        
50099         if(this.store){
50100             this.store.un('beforeload', this.onBeforeLoad, this);
50101             this.store.un('load', this.onLoad, this);
50102             this.store.un('loadexception', this.onLoadException, this);
50103         }
50104         //Roo.form.ComboBox.superclass.onDestroy.call(this);
50105     },
50106
50107     // private
50108     fireKey : function(e){
50109         if(e.isNavKeyPress() && !this.list.isVisible()){
50110             this.fireEvent("specialkey", this, e);
50111         }
50112     },
50113
50114     // private
50115     onResize: function(w, h){
50116         
50117         return; 
50118     
50119         
50120     },
50121
50122     /**
50123      * Allow or prevent the user from directly editing the field text.  If false is passed,
50124      * the user will only be able to select from the items defined in the dropdown list.  This method
50125      * is the runtime equivalent of setting the 'editable' config option at config time.
50126      * @param {Boolean} value True to allow the user to directly edit the field text
50127      */
50128     setEditable : function(value){
50129          
50130     },
50131
50132     // private
50133     onBeforeLoad : function(){
50134         
50135         Roo.log("Select before load");
50136         return;
50137     
50138         this.innerList.update(this.loadingText ?
50139                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
50140         //this.restrictHeight();
50141         this.selectedIndex = -1;
50142     },
50143
50144     // private
50145     onLoad : function(){
50146
50147     
50148         var dom = this.el.dom;
50149         dom.innerHTML = '';
50150          var od = dom.ownerDocument;
50151          
50152         if (this.emptyText) {
50153             var op = od.createElement('option');
50154             op.setAttribute('value', '');
50155             op.innerHTML = String.format('{0}', this.emptyText);
50156             dom.appendChild(op);
50157         }
50158         if(this.store.getCount() > 0){
50159            
50160             var vf = this.valueField;
50161             var df = this.displayField;
50162             this.store.data.each(function(r) {
50163                 // which colmsn to use... testing - cdoe / title..
50164                 var op = od.createElement('option');
50165                 op.setAttribute('value', r.data[vf]);
50166                 op.innerHTML = String.format('{0}', r.data[df]);
50167                 dom.appendChild(op);
50168             });
50169             if (typeof(this.defaultValue != 'undefined')) {
50170                 this.setValue(this.defaultValue);
50171             }
50172             
50173              
50174         }else{
50175             //this.onEmptyResults();
50176         }
50177         //this.el.focus();
50178     },
50179     // private
50180     onLoadException : function()
50181     {
50182         dom.innerHTML = '';
50183             
50184         Roo.log("Select on load exception");
50185         return;
50186     
50187         this.collapse();
50188         Roo.log(this.store.reader.jsonData);
50189         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50190             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50191         }
50192         
50193         
50194     },
50195     // private
50196     onTypeAhead : function(){
50197          
50198     },
50199
50200     // private
50201     onSelect : function(record, index){
50202         Roo.log('on select?');
50203         return;
50204         if(this.fireEvent('beforeselect', this, record, index) !== false){
50205             this.setFromData(index > -1 ? record.data : false);
50206             this.collapse();
50207             this.fireEvent('select', this, record, index);
50208         }
50209     },
50210
50211     /**
50212      * Returns the currently selected field value or empty string if no value is set.
50213      * @return {String} value The selected value
50214      */
50215     getValue : function(){
50216         var dom = this.el.dom;
50217         this.value = dom.options[dom.selectedIndex].value;
50218         return this.value;
50219         
50220     },
50221
50222     /**
50223      * Clears any text/value currently set in the field
50224      */
50225     clearValue : function(){
50226         this.value = '';
50227         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50228         
50229     },
50230
50231     /**
50232      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50233      * will be displayed in the field.  If the value does not match the data value of an existing item,
50234      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50235      * Otherwise the field will be blank (although the value will still be set).
50236      * @param {String} value The value to match
50237      */
50238     setValue : function(v){
50239         var d = this.el.dom;
50240         for (var i =0; i < d.options.length;i++) {
50241             if (v == d.options[i].value) {
50242                 d.selectedIndex = i;
50243                 this.value = v;
50244                 return;
50245             }
50246         }
50247         this.clearValue();
50248     },
50249     /**
50250      * @property {Object} the last set data for the element
50251      */
50252     
50253     lastData : false,
50254     /**
50255      * Sets the value of the field based on a object which is related to the record format for the store.
50256      * @param {Object} value the value to set as. or false on reset?
50257      */
50258     setFromData : function(o){
50259         Roo.log('setfrom data?');
50260          
50261         
50262         
50263     },
50264     // private
50265     reset : function(){
50266         this.clearValue();
50267     },
50268     // private
50269     findRecord : function(prop, value){
50270         
50271         return false;
50272     
50273         var record;
50274         if(this.store.getCount() > 0){
50275             this.store.each(function(r){
50276                 if(r.data[prop] == value){
50277                     record = r;
50278                     return false;
50279                 }
50280                 return true;
50281             });
50282         }
50283         return record;
50284     },
50285     
50286     getName: function()
50287     {
50288         // returns hidden if it's set..
50289         if (!this.rendered) {return ''};
50290         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50291         
50292     },
50293      
50294
50295     
50296
50297     // private
50298     onEmptyResults : function(){
50299         Roo.log('empty results');
50300         //this.collapse();
50301     },
50302
50303     /**
50304      * Returns true if the dropdown list is expanded, else false.
50305      */
50306     isExpanded : function(){
50307         return false;
50308     },
50309
50310     /**
50311      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50312      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50313      * @param {String} value The data value of the item to select
50314      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50315      * selected item if it is not currently in view (defaults to true)
50316      * @return {Boolean} True if the value matched an item in the list, else false
50317      */
50318     selectByValue : function(v, scrollIntoView){
50319         Roo.log('select By Value');
50320         return false;
50321     
50322         if(v !== undefined && v !== null){
50323             var r = this.findRecord(this.valueField || this.displayField, v);
50324             if(r){
50325                 this.select(this.store.indexOf(r), scrollIntoView);
50326                 return true;
50327             }
50328         }
50329         return false;
50330     },
50331
50332     /**
50333      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50334      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50335      * @param {Number} index The zero-based index of the list item to select
50336      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50337      * selected item if it is not currently in view (defaults to true)
50338      */
50339     select : function(index, scrollIntoView){
50340         Roo.log('select ');
50341         return  ;
50342         
50343         this.selectedIndex = index;
50344         this.view.select(index);
50345         if(scrollIntoView !== false){
50346             var el = this.view.getNode(index);
50347             if(el){
50348                 this.innerList.scrollChildIntoView(el, false);
50349             }
50350         }
50351     },
50352
50353       
50354
50355     // private
50356     validateBlur : function(){
50357         
50358         return;
50359         
50360     },
50361
50362     // private
50363     initQuery : function(){
50364         this.doQuery(this.getRawValue());
50365     },
50366
50367     // private
50368     doForce : function(){
50369         if(this.el.dom.value.length > 0){
50370             this.el.dom.value =
50371                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50372              
50373         }
50374     },
50375
50376     /**
50377      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50378      * query allowing the query action to be canceled if needed.
50379      * @param {String} query The SQL query to execute
50380      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50381      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50382      * saved in the current store (defaults to false)
50383      */
50384     doQuery : function(q, forceAll){
50385         
50386         Roo.log('doQuery?');
50387         if(q === undefined || q === null){
50388             q = '';
50389         }
50390         var qe = {
50391             query: q,
50392             forceAll: forceAll,
50393             combo: this,
50394             cancel:false
50395         };
50396         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50397             return false;
50398         }
50399         q = qe.query;
50400         forceAll = qe.forceAll;
50401         if(forceAll === true || (q.length >= this.minChars)){
50402             if(this.lastQuery != q || this.alwaysQuery){
50403                 this.lastQuery = q;
50404                 if(this.mode == 'local'){
50405                     this.selectedIndex = -1;
50406                     if(forceAll){
50407                         this.store.clearFilter();
50408                     }else{
50409                         this.store.filter(this.displayField, q);
50410                     }
50411                     this.onLoad();
50412                 }else{
50413                     this.store.baseParams[this.queryParam] = q;
50414                     this.store.load({
50415                         params: this.getParams(q)
50416                     });
50417                     this.expand();
50418                 }
50419             }else{
50420                 this.selectedIndex = -1;
50421                 this.onLoad();   
50422             }
50423         }
50424     },
50425
50426     // private
50427     getParams : function(q){
50428         var p = {};
50429         //p[this.queryParam] = q;
50430         if(this.pageSize){
50431             p.start = 0;
50432             p.limit = this.pageSize;
50433         }
50434         return p;
50435     },
50436
50437     /**
50438      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50439      */
50440     collapse : function(){
50441         
50442     },
50443
50444     // private
50445     collapseIf : function(e){
50446         
50447     },
50448
50449     /**
50450      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50451      */
50452     expand : function(){
50453         
50454     } ,
50455
50456     // private
50457      
50458
50459     /** 
50460     * @cfg {Boolean} grow 
50461     * @hide 
50462     */
50463     /** 
50464     * @cfg {Number} growMin 
50465     * @hide 
50466     */
50467     /** 
50468     * @cfg {Number} growMax 
50469     * @hide 
50470     */
50471     /**
50472      * @hide
50473      * @method autoSize
50474      */
50475     
50476     setWidth : function()
50477     {
50478         
50479     },
50480     getResizeEl : function(){
50481         return this.el;
50482     }
50483 });//<script type="text/javasscript">
50484  
50485
50486 /**
50487  * @class Roo.DDView
50488  * A DnD enabled version of Roo.View.
50489  * @param {Element/String} container The Element in which to create the View.
50490  * @param {String} tpl The template string used to create the markup for each element of the View
50491  * @param {Object} config The configuration properties. These include all the config options of
50492  * {@link Roo.View} plus some specific to this class.<br>
50493  * <p>
50494  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50495  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50496  * <p>
50497  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50498 .x-view-drag-insert-above {
50499         border-top:1px dotted #3366cc;
50500 }
50501 .x-view-drag-insert-below {
50502         border-bottom:1px dotted #3366cc;
50503 }
50504 </code></pre>
50505  * 
50506  */
50507  
50508 Roo.DDView = function(container, tpl, config) {
50509     Roo.DDView.superclass.constructor.apply(this, arguments);
50510     this.getEl().setStyle("outline", "0px none");
50511     this.getEl().unselectable();
50512     if (this.dragGroup) {
50513                 this.setDraggable(this.dragGroup.split(","));
50514     }
50515     if (this.dropGroup) {
50516                 this.setDroppable(this.dropGroup.split(","));
50517     }
50518     if (this.deletable) {
50519         this.setDeletable();
50520     }
50521     this.isDirtyFlag = false;
50522         this.addEvents({
50523                 "drop" : true
50524         });
50525 };
50526
50527 Roo.extend(Roo.DDView, Roo.View, {
50528 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50529 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50530 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50531 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50532
50533         isFormField: true,
50534
50535         reset: Roo.emptyFn,
50536         
50537         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50538
50539         validate: function() {
50540                 return true;
50541         },
50542         
50543         destroy: function() {
50544                 this.purgeListeners();
50545                 this.getEl.removeAllListeners();
50546                 this.getEl().remove();
50547                 if (this.dragZone) {
50548                         if (this.dragZone.destroy) {
50549                                 this.dragZone.destroy();
50550                         }
50551                 }
50552                 if (this.dropZone) {
50553                         if (this.dropZone.destroy) {
50554                                 this.dropZone.destroy();
50555                         }
50556                 }
50557         },
50558
50559 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50560         getName: function() {
50561                 return this.name;
50562         },
50563
50564 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50565         setValue: function(v) {
50566                 if (!this.store) {
50567                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50568                 }
50569                 var data = {};
50570                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50571                 this.store.proxy = new Roo.data.MemoryProxy(data);
50572                 this.store.load();
50573         },
50574
50575 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50576         getValue: function() {
50577                 var result = '(';
50578                 this.store.each(function(rec) {
50579                         result += rec.id + ',';
50580                 });
50581                 return result.substr(0, result.length - 1) + ')';
50582         },
50583         
50584         getIds: function() {
50585                 var i = 0, result = new Array(this.store.getCount());
50586                 this.store.each(function(rec) {
50587                         result[i++] = rec.id;
50588                 });
50589                 return result;
50590         },
50591         
50592         isDirty: function() {
50593                 return this.isDirtyFlag;
50594         },
50595
50596 /**
50597  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50598  *      whole Element becomes the target, and this causes the drop gesture to append.
50599  */
50600     getTargetFromEvent : function(e) {
50601                 var target = e.getTarget();
50602                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50603                 target = target.parentNode;
50604                 }
50605                 if (!target) {
50606                         target = this.el.dom.lastChild || this.el.dom;
50607                 }
50608                 return target;
50609     },
50610
50611 /**
50612  *      Create the drag data which consists of an object which has the property "ddel" as
50613  *      the drag proxy element. 
50614  */
50615     getDragData : function(e) {
50616         var target = this.findItemFromChild(e.getTarget());
50617                 if(target) {
50618                         this.handleSelection(e);
50619                         var selNodes = this.getSelectedNodes();
50620             var dragData = {
50621                 source: this,
50622                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50623                 nodes: selNodes,
50624                 records: []
50625                         };
50626                         var selectedIndices = this.getSelectedIndexes();
50627                         for (var i = 0; i < selectedIndices.length; i++) {
50628                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50629                         }
50630                         if (selNodes.length == 1) {
50631                                 dragData.ddel = target.cloneNode(true); // the div element
50632                         } else {
50633                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50634                                 div.className = 'multi-proxy';
50635                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50636                                         div.appendChild(selNodes[i].cloneNode(true));
50637                                 }
50638                                 dragData.ddel = div;
50639                         }
50640             //console.log(dragData)
50641             //console.log(dragData.ddel.innerHTML)
50642                         return dragData;
50643                 }
50644         //console.log('nodragData')
50645                 return false;
50646     },
50647     
50648 /**     Specify to which ddGroup items in this DDView may be dragged. */
50649     setDraggable: function(ddGroup) {
50650         if (ddGroup instanceof Array) {
50651                 Roo.each(ddGroup, this.setDraggable, this);
50652                 return;
50653         }
50654         if (this.dragZone) {
50655                 this.dragZone.addToGroup(ddGroup);
50656         } else {
50657                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
50658                                 containerScroll: true,
50659                                 ddGroup: ddGroup 
50660
50661                         });
50662 //                      Draggability implies selection. DragZone's mousedown selects the element.
50663                         if (!this.multiSelect) { this.singleSelect = true; }
50664
50665 //                      Wire the DragZone's handlers up to methods in *this*
50666                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
50667                 }
50668     },
50669
50670 /**     Specify from which ddGroup this DDView accepts drops. */
50671     setDroppable: function(ddGroup) {
50672         if (ddGroup instanceof Array) {
50673                 Roo.each(ddGroup, this.setDroppable, this);
50674                 return;
50675         }
50676         if (this.dropZone) {
50677                 this.dropZone.addToGroup(ddGroup);
50678         } else {
50679                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
50680                                 containerScroll: true,
50681                                 ddGroup: ddGroup
50682                         });
50683
50684 //                      Wire the DropZone's handlers up to methods in *this*
50685                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
50686                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
50687                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
50688                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
50689                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
50690                 }
50691     },
50692
50693 /**     Decide whether to drop above or below a View node. */
50694     getDropPoint : function(e, n, dd){
50695         if (n == this.el.dom) { return "above"; }
50696                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
50697                 var c = t + (b - t) / 2;
50698                 var y = Roo.lib.Event.getPageY(e);
50699                 if(y <= c) {
50700                         return "above";
50701                 }else{
50702                         return "below";
50703                 }
50704     },
50705
50706     onNodeEnter : function(n, dd, e, data){
50707                 return false;
50708     },
50709     
50710     onNodeOver : function(n, dd, e, data){
50711                 var pt = this.getDropPoint(e, n, dd);
50712                 // set the insert point style on the target node
50713                 var dragElClass = this.dropNotAllowed;
50714                 if (pt) {
50715                         var targetElClass;
50716                         if (pt == "above"){
50717                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
50718                                 targetElClass = "x-view-drag-insert-above";
50719                         } else {
50720                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
50721                                 targetElClass = "x-view-drag-insert-below";
50722                         }
50723                         if (this.lastInsertClass != targetElClass){
50724                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
50725                                 this.lastInsertClass = targetElClass;
50726                         }
50727                 }
50728                 return dragElClass;
50729         },
50730
50731     onNodeOut : function(n, dd, e, data){
50732                 this.removeDropIndicators(n);
50733     },
50734
50735     onNodeDrop : function(n, dd, e, data){
50736         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
50737                 return false;
50738         }
50739         var pt = this.getDropPoint(e, n, dd);
50740                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
50741                 if (pt == "below") { insertAt++; }
50742                 for (var i = 0; i < data.records.length; i++) {
50743                         var r = data.records[i];
50744                         var dup = this.store.getById(r.id);
50745                         if (dup && (dd != this.dragZone)) {
50746                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
50747                         } else {
50748                                 if (data.copy) {
50749                                         this.store.insert(insertAt++, r.copy());
50750                                 } else {
50751                                         data.source.isDirtyFlag = true;
50752                                         r.store.remove(r);
50753                                         this.store.insert(insertAt++, r);
50754                                 }
50755                                 this.isDirtyFlag = true;
50756                         }
50757                 }
50758                 this.dragZone.cachedTarget = null;
50759                 return true;
50760     },
50761
50762     removeDropIndicators : function(n){
50763                 if(n){
50764                         Roo.fly(n).removeClass([
50765                                 "x-view-drag-insert-above",
50766                                 "x-view-drag-insert-below"]);
50767                         this.lastInsertClass = "_noclass";
50768                 }
50769     },
50770
50771 /**
50772  *      Utility method. Add a delete option to the DDView's context menu.
50773  *      @param {String} imageUrl The URL of the "delete" icon image.
50774  */
50775         setDeletable: function(imageUrl) {
50776                 if (!this.singleSelect && !this.multiSelect) {
50777                         this.singleSelect = true;
50778                 }
50779                 var c = this.getContextMenu();
50780                 this.contextMenu.on("itemclick", function(item) {
50781                         switch (item.id) {
50782                                 case "delete":
50783                                         this.remove(this.getSelectedIndexes());
50784                                         break;
50785                         }
50786                 }, this);
50787                 this.contextMenu.add({
50788                         icon: imageUrl,
50789                         id: "delete",
50790                         text: 'Delete'
50791                 });
50792         },
50793         
50794 /**     Return the context menu for this DDView. */
50795         getContextMenu: function() {
50796                 if (!this.contextMenu) {
50797 //                      Create the View's context menu
50798                         this.contextMenu = new Roo.menu.Menu({
50799                                 id: this.id + "-contextmenu"
50800                         });
50801                         this.el.on("contextmenu", this.showContextMenu, this);
50802                 }
50803                 return this.contextMenu;
50804         },
50805         
50806         disableContextMenu: function() {
50807                 if (this.contextMenu) {
50808                         this.el.un("contextmenu", this.showContextMenu, this);
50809                 }
50810         },
50811
50812         showContextMenu: function(e, item) {
50813         item = this.findItemFromChild(e.getTarget());
50814                 if (item) {
50815                         e.stopEvent();
50816                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
50817                         this.contextMenu.showAt(e.getXY());
50818             }
50819     },
50820
50821 /**
50822  *      Remove {@link Roo.data.Record}s at the specified indices.
50823  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
50824  */
50825     remove: function(selectedIndices) {
50826                 selectedIndices = [].concat(selectedIndices);
50827                 for (var i = 0; i < selectedIndices.length; i++) {
50828                         var rec = this.store.getAt(selectedIndices[i]);
50829                         this.store.remove(rec);
50830                 }
50831     },
50832
50833 /**
50834  *      Double click fires the event, but also, if this is draggable, and there is only one other
50835  *      related DropZone, it transfers the selected node.
50836  */
50837     onDblClick : function(e){
50838         var item = this.findItemFromChild(e.getTarget());
50839         if(item){
50840             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
50841                 return false;
50842             }
50843             if (this.dragGroup) {
50844                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
50845                     while (targets.indexOf(this.dropZone) > -1) {
50846                             targets.remove(this.dropZone);
50847                                 }
50848                     if (targets.length == 1) {
50849                                         this.dragZone.cachedTarget = null;
50850                         var el = Roo.get(targets[0].getEl());
50851                         var box = el.getBox(true);
50852                         targets[0].onNodeDrop(el.dom, {
50853                                 target: el.dom,
50854                                 xy: [box.x, box.y + box.height - 1]
50855                         }, null, this.getDragData(e));
50856                     }
50857                 }
50858         }
50859     },
50860     
50861     handleSelection: function(e) {
50862                 this.dragZone.cachedTarget = null;
50863         var item = this.findItemFromChild(e.getTarget());
50864         if (!item) {
50865                 this.clearSelections(true);
50866                 return;
50867         }
50868                 if (item && (this.multiSelect || this.singleSelect)){
50869                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
50870                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
50871                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
50872                                 this.unselect(item);
50873                         } else {
50874                                 this.select(item, this.multiSelect && e.ctrlKey);
50875                                 this.lastSelection = item;
50876                         }
50877                 }
50878     },
50879
50880     onItemClick : function(item, index, e){
50881                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
50882                         return false;
50883                 }
50884                 return true;
50885     },
50886
50887     unselect : function(nodeInfo, suppressEvent){
50888                 var node = this.getNode(nodeInfo);
50889                 if(node && this.isSelected(node)){
50890                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
50891                                 Roo.fly(node).removeClass(this.selectedClass);
50892                                 this.selections.remove(node);
50893                                 if(!suppressEvent){
50894                                         this.fireEvent("selectionchange", this, this.selections);
50895                                 }
50896                         }
50897                 }
50898     }
50899 });
50900 /*
50901  * Based on:
50902  * Ext JS Library 1.1.1
50903  * Copyright(c) 2006-2007, Ext JS, LLC.
50904  *
50905  * Originally Released Under LGPL - original licence link has changed is not relivant.
50906  *
50907  * Fork - LGPL
50908  * <script type="text/javascript">
50909  */
50910  
50911 /**
50912  * @class Roo.LayoutManager
50913  * @extends Roo.util.Observable
50914  * Base class for layout managers.
50915  */
50916 Roo.LayoutManager = function(container, config){
50917     Roo.LayoutManager.superclass.constructor.call(this);
50918     this.el = Roo.get(container);
50919     // ie scrollbar fix
50920     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
50921         document.body.scroll = "no";
50922     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
50923         this.el.position('relative');
50924     }
50925     this.id = this.el.id;
50926     this.el.addClass("x-layout-container");
50927     /** false to disable window resize monitoring @type Boolean */
50928     this.monitorWindowResize = true;
50929     this.regions = {};
50930     this.addEvents({
50931         /**
50932          * @event layout
50933          * Fires when a layout is performed. 
50934          * @param {Roo.LayoutManager} this
50935          */
50936         "layout" : true,
50937         /**
50938          * @event regionresized
50939          * Fires when the user resizes a region. 
50940          * @param {Roo.LayoutRegion} region The resized region
50941          * @param {Number} newSize The new size (width for east/west, height for north/south)
50942          */
50943         "regionresized" : true,
50944         /**
50945          * @event regioncollapsed
50946          * Fires when a region is collapsed. 
50947          * @param {Roo.LayoutRegion} region The collapsed region
50948          */
50949         "regioncollapsed" : true,
50950         /**
50951          * @event regionexpanded
50952          * Fires when a region is expanded.  
50953          * @param {Roo.LayoutRegion} region The expanded region
50954          */
50955         "regionexpanded" : true
50956     });
50957     this.updating = false;
50958     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50959 };
50960
50961 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
50962     /**
50963      * Returns true if this layout is currently being updated
50964      * @return {Boolean}
50965      */
50966     isUpdating : function(){
50967         return this.updating; 
50968     },
50969     
50970     /**
50971      * Suspend the LayoutManager from doing auto-layouts while
50972      * making multiple add or remove calls
50973      */
50974     beginUpdate : function(){
50975         this.updating = true;    
50976     },
50977     
50978     /**
50979      * Restore auto-layouts and optionally disable the manager from performing a layout
50980      * @param {Boolean} noLayout true to disable a layout update 
50981      */
50982     endUpdate : function(noLayout){
50983         this.updating = false;
50984         if(!noLayout){
50985             this.layout();
50986         }    
50987     },
50988     
50989     layout: function(){
50990         
50991     },
50992     
50993     onRegionResized : function(region, newSize){
50994         this.fireEvent("regionresized", region, newSize);
50995         this.layout();
50996     },
50997     
50998     onRegionCollapsed : function(region){
50999         this.fireEvent("regioncollapsed", region);
51000     },
51001     
51002     onRegionExpanded : function(region){
51003         this.fireEvent("regionexpanded", region);
51004     },
51005         
51006     /**
51007      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
51008      * performs box-model adjustments.
51009      * @return {Object} The size as an object {width: (the width), height: (the height)}
51010      */
51011     getViewSize : function(){
51012         var size;
51013         if(this.el.dom != document.body){
51014             size = this.el.getSize();
51015         }else{
51016             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
51017         }
51018         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
51019         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
51020         return size;
51021     },
51022     
51023     /**
51024      * Returns the Element this layout is bound to.
51025      * @return {Roo.Element}
51026      */
51027     getEl : function(){
51028         return this.el;
51029     },
51030     
51031     /**
51032      * Returns the specified region.
51033      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
51034      * @return {Roo.LayoutRegion}
51035      */
51036     getRegion : function(target){
51037         return this.regions[target.toLowerCase()];
51038     },
51039     
51040     onWindowResize : function(){
51041         if(this.monitorWindowResize){
51042             this.layout();
51043         }
51044     }
51045 });/*
51046  * Based on:
51047  * Ext JS Library 1.1.1
51048  * Copyright(c) 2006-2007, Ext JS, LLC.
51049  *
51050  * Originally Released Under LGPL - original licence link has changed is not relivant.
51051  *
51052  * Fork - LGPL
51053  * <script type="text/javascript">
51054  */
51055 /**
51056  * @class Roo.BorderLayout
51057  * @extends Roo.LayoutManager
51058  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
51059  * please see: <br><br>
51060  * <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>
51061  * <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>
51062  * Example:
51063  <pre><code>
51064  var layout = new Roo.BorderLayout(document.body, {
51065     north: {
51066         initialSize: 25,
51067         titlebar: false
51068     },
51069     west: {
51070         split:true,
51071         initialSize: 200,
51072         minSize: 175,
51073         maxSize: 400,
51074         titlebar: true,
51075         collapsible: true
51076     },
51077     east: {
51078         split:true,
51079         initialSize: 202,
51080         minSize: 175,
51081         maxSize: 400,
51082         titlebar: true,
51083         collapsible: true
51084     },
51085     south: {
51086         split:true,
51087         initialSize: 100,
51088         minSize: 100,
51089         maxSize: 200,
51090         titlebar: true,
51091         collapsible: true
51092     },
51093     center: {
51094         titlebar: true,
51095         autoScroll:true,
51096         resizeTabs: true,
51097         minTabWidth: 50,
51098         preferredTabWidth: 150
51099     }
51100 });
51101
51102 // shorthand
51103 var CP = Roo.ContentPanel;
51104
51105 layout.beginUpdate();
51106 layout.add("north", new CP("north", "North"));
51107 layout.add("south", new CP("south", {title: "South", closable: true}));
51108 layout.add("west", new CP("west", {title: "West"}));
51109 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
51110 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
51111 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
51112 layout.getRegion("center").showPanel("center1");
51113 layout.endUpdate();
51114 </code></pre>
51115
51116 <b>The container the layout is rendered into can be either the body element or any other element.
51117 If it is not the body element, the container needs to either be an absolute positioned element,
51118 or you will need to add "position:relative" to the css of the container.  You will also need to specify
51119 the container size if it is not the body element.</b>
51120
51121 * @constructor
51122 * Create a new BorderLayout
51123 * @param {String/HTMLElement/Element} container The container this layout is bound to
51124 * @param {Object} config Configuration options
51125  */
51126 Roo.BorderLayout = function(container, config){
51127     config = config || {};
51128     Roo.BorderLayout.superclass.constructor.call(this, container, config);
51129     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
51130     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
51131         var target = this.factory.validRegions[i];
51132         if(config[target]){
51133             this.addRegion(target, config[target]);
51134         }
51135     }
51136 };
51137
51138 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
51139     /**
51140      * Creates and adds a new region if it doesn't already exist.
51141      * @param {String} target The target region key (north, south, east, west or center).
51142      * @param {Object} config The regions config object
51143      * @return {BorderLayoutRegion} The new region
51144      */
51145     addRegion : function(target, config){
51146         if(!this.regions[target]){
51147             var r = this.factory.create(target, this, config);
51148             this.bindRegion(target, r);
51149         }
51150         return this.regions[target];
51151     },
51152
51153     // private (kinda)
51154     bindRegion : function(name, r){
51155         this.regions[name] = r;
51156         r.on("visibilitychange", this.layout, this);
51157         r.on("paneladded", this.layout, this);
51158         r.on("panelremoved", this.layout, this);
51159         r.on("invalidated", this.layout, this);
51160         r.on("resized", this.onRegionResized, this);
51161         r.on("collapsed", this.onRegionCollapsed, this);
51162         r.on("expanded", this.onRegionExpanded, this);
51163     },
51164
51165     /**
51166      * Performs a layout update.
51167      */
51168     layout : function(){
51169         if(this.updating) {
51170             return;
51171         }
51172         var size = this.getViewSize();
51173         var w = size.width;
51174         var h = size.height;
51175         var centerW = w;
51176         var centerH = h;
51177         var centerY = 0;
51178         var centerX = 0;
51179         //var x = 0, y = 0;
51180
51181         var rs = this.regions;
51182         var north = rs["north"];
51183         var south = rs["south"]; 
51184         var west = rs["west"];
51185         var east = rs["east"];
51186         var center = rs["center"];
51187         //if(this.hideOnLayout){ // not supported anymore
51188             //c.el.setStyle("display", "none");
51189         //}
51190         if(north && north.isVisible()){
51191             var b = north.getBox();
51192             var m = north.getMargins();
51193             b.width = w - (m.left+m.right);
51194             b.x = m.left;
51195             b.y = m.top;
51196             centerY = b.height + b.y + m.bottom;
51197             centerH -= centerY;
51198             north.updateBox(this.safeBox(b));
51199         }
51200         if(south && south.isVisible()){
51201             var b = south.getBox();
51202             var m = south.getMargins();
51203             b.width = w - (m.left+m.right);
51204             b.x = m.left;
51205             var totalHeight = (b.height + m.top + m.bottom);
51206             b.y = h - totalHeight + m.top;
51207             centerH -= totalHeight;
51208             south.updateBox(this.safeBox(b));
51209         }
51210         if(west && west.isVisible()){
51211             var b = west.getBox();
51212             var m = west.getMargins();
51213             b.height = centerH - (m.top+m.bottom);
51214             b.x = m.left;
51215             b.y = centerY + m.top;
51216             var totalWidth = (b.width + m.left + m.right);
51217             centerX += totalWidth;
51218             centerW -= totalWidth;
51219             west.updateBox(this.safeBox(b));
51220         }
51221         if(east && east.isVisible()){
51222             var b = east.getBox();
51223             var m = east.getMargins();
51224             b.height = centerH - (m.top+m.bottom);
51225             var totalWidth = (b.width + m.left + m.right);
51226             b.x = w - totalWidth + m.left;
51227             b.y = centerY + m.top;
51228             centerW -= totalWidth;
51229             east.updateBox(this.safeBox(b));
51230         }
51231         if(center){
51232             var m = center.getMargins();
51233             var centerBox = {
51234                 x: centerX + m.left,
51235                 y: centerY + m.top,
51236                 width: centerW - (m.left+m.right),
51237                 height: centerH - (m.top+m.bottom)
51238             };
51239             //if(this.hideOnLayout){
51240                 //center.el.setStyle("display", "block");
51241             //}
51242             center.updateBox(this.safeBox(centerBox));
51243         }
51244         this.el.repaint();
51245         this.fireEvent("layout", this);
51246     },
51247
51248     // private
51249     safeBox : function(box){
51250         box.width = Math.max(0, box.width);
51251         box.height = Math.max(0, box.height);
51252         return box;
51253     },
51254
51255     /**
51256      * Adds a ContentPanel (or subclass) to this layout.
51257      * @param {String} target The target region key (north, south, east, west or center).
51258      * @param {Roo.ContentPanel} panel The panel to add
51259      * @return {Roo.ContentPanel} The added panel
51260      */
51261     add : function(target, panel){
51262          
51263         target = target.toLowerCase();
51264         return this.regions[target].add(panel);
51265     },
51266
51267     /**
51268      * Remove a ContentPanel (or subclass) to this layout.
51269      * @param {String} target The target region key (north, south, east, west or center).
51270      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51271      * @return {Roo.ContentPanel} The removed panel
51272      */
51273     remove : function(target, panel){
51274         target = target.toLowerCase();
51275         return this.regions[target].remove(panel);
51276     },
51277
51278     /**
51279      * Searches all regions for a panel with the specified id
51280      * @param {String} panelId
51281      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51282      */
51283     findPanel : function(panelId){
51284         var rs = this.regions;
51285         for(var target in rs){
51286             if(typeof rs[target] != "function"){
51287                 var p = rs[target].getPanel(panelId);
51288                 if(p){
51289                     return p;
51290                 }
51291             }
51292         }
51293         return null;
51294     },
51295
51296     /**
51297      * Searches all regions for a panel with the specified id and activates (shows) it.
51298      * @param {String/ContentPanel} panelId The panels id or the panel itself
51299      * @return {Roo.ContentPanel} The shown panel or null
51300      */
51301     showPanel : function(panelId) {
51302       var rs = this.regions;
51303       for(var target in rs){
51304          var r = rs[target];
51305          if(typeof r != "function"){
51306             if(r.hasPanel(panelId)){
51307                return r.showPanel(panelId);
51308             }
51309          }
51310       }
51311       return null;
51312    },
51313
51314    /**
51315      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51316      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51317      */
51318     restoreState : function(provider){
51319         if(!provider){
51320             provider = Roo.state.Manager;
51321         }
51322         var sm = new Roo.LayoutStateManager();
51323         sm.init(this, provider);
51324     },
51325
51326     /**
51327      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51328      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51329      * a valid ContentPanel config object.  Example:
51330      * <pre><code>
51331 // Create the main layout
51332 var layout = new Roo.BorderLayout('main-ct', {
51333     west: {
51334         split:true,
51335         minSize: 175,
51336         titlebar: true
51337     },
51338     center: {
51339         title:'Components'
51340     }
51341 }, 'main-ct');
51342
51343 // Create and add multiple ContentPanels at once via configs
51344 layout.batchAdd({
51345    west: {
51346        id: 'source-files',
51347        autoCreate:true,
51348        title:'Ext Source Files',
51349        autoScroll:true,
51350        fitToFrame:true
51351    },
51352    center : {
51353        el: cview,
51354        autoScroll:true,
51355        fitToFrame:true,
51356        toolbar: tb,
51357        resizeEl:'cbody'
51358    }
51359 });
51360 </code></pre>
51361      * @param {Object} regions An object containing ContentPanel configs by region name
51362      */
51363     batchAdd : function(regions){
51364         this.beginUpdate();
51365         for(var rname in regions){
51366             var lr = this.regions[rname];
51367             if(lr){
51368                 this.addTypedPanels(lr, regions[rname]);
51369             }
51370         }
51371         this.endUpdate();
51372     },
51373
51374     // private
51375     addTypedPanels : function(lr, ps){
51376         if(typeof ps == 'string'){
51377             lr.add(new Roo.ContentPanel(ps));
51378         }
51379         else if(ps instanceof Array){
51380             for(var i =0, len = ps.length; i < len; i++){
51381                 this.addTypedPanels(lr, ps[i]);
51382             }
51383         }
51384         else if(!ps.events){ // raw config?
51385             var el = ps.el;
51386             delete ps.el; // prevent conflict
51387             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51388         }
51389         else {  // panel object assumed!
51390             lr.add(ps);
51391         }
51392     },
51393     /**
51394      * Adds a xtype elements to the layout.
51395      * <pre><code>
51396
51397 layout.addxtype({
51398        xtype : 'ContentPanel',
51399        region: 'west',
51400        items: [ .... ]
51401    }
51402 );
51403
51404 layout.addxtype({
51405         xtype : 'NestedLayoutPanel',
51406         region: 'west',
51407         layout: {
51408            center: { },
51409            west: { }   
51410         },
51411         items : [ ... list of content panels or nested layout panels.. ]
51412    }
51413 );
51414 </code></pre>
51415      * @param {Object} cfg Xtype definition of item to add.
51416      */
51417     addxtype : function(cfg)
51418     {
51419         // basically accepts a pannel...
51420         // can accept a layout region..!?!?
51421         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51422         
51423         if (!cfg.xtype.match(/Panel$/)) {
51424             return false;
51425         }
51426         var ret = false;
51427         
51428         if (typeof(cfg.region) == 'undefined') {
51429             Roo.log("Failed to add Panel, region was not set");
51430             Roo.log(cfg);
51431             return false;
51432         }
51433         var region = cfg.region;
51434         delete cfg.region;
51435         
51436           
51437         var xitems = [];
51438         if (cfg.items) {
51439             xitems = cfg.items;
51440             delete cfg.items;
51441         }
51442         var nb = false;
51443         
51444         switch(cfg.xtype) 
51445         {
51446             case 'ContentPanel':  // ContentPanel (el, cfg)
51447             case 'ScrollPanel':  // ContentPanel (el, cfg)
51448             case 'ViewPanel': 
51449                 if(cfg.autoCreate) {
51450                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51451                 } else {
51452                     var el = this.el.createChild();
51453                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51454                 }
51455                 
51456                 this.add(region, ret);
51457                 break;
51458             
51459             
51460             case 'TreePanel': // our new panel!
51461                 cfg.el = this.el.createChild();
51462                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51463                 this.add(region, ret);
51464                 break;
51465             
51466             case 'NestedLayoutPanel': 
51467                 // create a new Layout (which is  a Border Layout...
51468                 var el = this.el.createChild();
51469                 var clayout = cfg.layout;
51470                 delete cfg.layout;
51471                 clayout.items   = clayout.items  || [];
51472                 // replace this exitems with the clayout ones..
51473                 xitems = clayout.items;
51474                  
51475                 
51476                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51477                     cfg.background = false;
51478                 }
51479                 var layout = new Roo.BorderLayout(el, clayout);
51480                 
51481                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51482                 //console.log('adding nested layout panel '  + cfg.toSource());
51483                 this.add(region, ret);
51484                 nb = {}; /// find first...
51485                 break;
51486                 
51487             case 'GridPanel': 
51488             
51489                 // needs grid and region
51490                 
51491                 //var el = this.getRegion(region).el.createChild();
51492                 var el = this.el.createChild();
51493                 // create the grid first...
51494                 
51495                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51496                 delete cfg.grid;
51497                 if (region == 'center' && this.active ) {
51498                     cfg.background = false;
51499                 }
51500                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51501                 
51502                 this.add(region, ret);
51503                 if (cfg.background) {
51504                     ret.on('activate', function(gp) {
51505                         if (!gp.grid.rendered) {
51506                             gp.grid.render();
51507                         }
51508                     });
51509                 } else {
51510                     grid.render();
51511                 }
51512                 break;
51513            
51514            
51515            
51516                 
51517                 
51518                 
51519             default:
51520                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51521                     
51522                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51523                     this.add(region, ret);
51524                 } else {
51525                 
51526                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51527                     return null;
51528                 }
51529                 
51530              // GridPanel (grid, cfg)
51531             
51532         }
51533         this.beginUpdate();
51534         // add children..
51535         var region = '';
51536         var abn = {};
51537         Roo.each(xitems, function(i)  {
51538             region = nb && i.region ? i.region : false;
51539             
51540             var add = ret.addxtype(i);
51541            
51542             if (region) {
51543                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51544                 if (!i.background) {
51545                     abn[region] = nb[region] ;
51546                 }
51547             }
51548             
51549         });
51550         this.endUpdate();
51551
51552         // make the last non-background panel active..
51553         //if (nb) { Roo.log(abn); }
51554         if (nb) {
51555             
51556             for(var r in abn) {
51557                 region = this.getRegion(r);
51558                 if (region) {
51559                     // tried using nb[r], but it does not work..
51560                      
51561                     region.showPanel(abn[r]);
51562                    
51563                 }
51564             }
51565         }
51566         return ret;
51567         
51568     }
51569 });
51570
51571 /**
51572  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51573  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51574  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51575  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51576  * <pre><code>
51577 // shorthand
51578 var CP = Roo.ContentPanel;
51579
51580 var layout = Roo.BorderLayout.create({
51581     north: {
51582         initialSize: 25,
51583         titlebar: false,
51584         panels: [new CP("north", "North")]
51585     },
51586     west: {
51587         split:true,
51588         initialSize: 200,
51589         minSize: 175,
51590         maxSize: 400,
51591         titlebar: true,
51592         collapsible: true,
51593         panels: [new CP("west", {title: "West"})]
51594     },
51595     east: {
51596         split:true,
51597         initialSize: 202,
51598         minSize: 175,
51599         maxSize: 400,
51600         titlebar: true,
51601         collapsible: true,
51602         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51603     },
51604     south: {
51605         split:true,
51606         initialSize: 100,
51607         minSize: 100,
51608         maxSize: 200,
51609         titlebar: true,
51610         collapsible: true,
51611         panels: [new CP("south", {title: "South", closable: true})]
51612     },
51613     center: {
51614         titlebar: true,
51615         autoScroll:true,
51616         resizeTabs: true,
51617         minTabWidth: 50,
51618         preferredTabWidth: 150,
51619         panels: [
51620             new CP("center1", {title: "Close Me", closable: true}),
51621             new CP("center2", {title: "Center Panel", closable: false})
51622         ]
51623     }
51624 }, document.body);
51625
51626 layout.getRegion("center").showPanel("center1");
51627 </code></pre>
51628  * @param config
51629  * @param targetEl
51630  */
51631 Roo.BorderLayout.create = function(config, targetEl){
51632     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51633     layout.beginUpdate();
51634     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51635     for(var j = 0, jlen = regions.length; j < jlen; j++){
51636         var lr = regions[j];
51637         if(layout.regions[lr] && config[lr].panels){
51638             var r = layout.regions[lr];
51639             var ps = config[lr].panels;
51640             layout.addTypedPanels(r, ps);
51641         }
51642     }
51643     layout.endUpdate();
51644     return layout;
51645 };
51646
51647 // private
51648 Roo.BorderLayout.RegionFactory = {
51649     // private
51650     validRegions : ["north","south","east","west","center"],
51651
51652     // private
51653     create : function(target, mgr, config){
51654         target = target.toLowerCase();
51655         if(config.lightweight || config.basic){
51656             return new Roo.BasicLayoutRegion(mgr, config, target);
51657         }
51658         switch(target){
51659             case "north":
51660                 return new Roo.NorthLayoutRegion(mgr, config);
51661             case "south":
51662                 return new Roo.SouthLayoutRegion(mgr, config);
51663             case "east":
51664                 return new Roo.EastLayoutRegion(mgr, config);
51665             case "west":
51666                 return new Roo.WestLayoutRegion(mgr, config);
51667             case "center":
51668                 return new Roo.CenterLayoutRegion(mgr, config);
51669         }
51670         throw 'Layout region "'+target+'" not supported.';
51671     }
51672 };/*
51673  * Based on:
51674  * Ext JS Library 1.1.1
51675  * Copyright(c) 2006-2007, Ext JS, LLC.
51676  *
51677  * Originally Released Under LGPL - original licence link has changed is not relivant.
51678  *
51679  * Fork - LGPL
51680  * <script type="text/javascript">
51681  */
51682  
51683 /**
51684  * @class Roo.BasicLayoutRegion
51685  * @extends Roo.util.Observable
51686  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
51687  * and does not have a titlebar, tabs or any other features. All it does is size and position 
51688  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
51689  */
51690 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
51691     this.mgr = mgr;
51692     this.position  = pos;
51693     this.events = {
51694         /**
51695          * @scope Roo.BasicLayoutRegion
51696          */
51697         
51698         /**
51699          * @event beforeremove
51700          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
51701          * @param {Roo.LayoutRegion} this
51702          * @param {Roo.ContentPanel} panel The panel
51703          * @param {Object} e The cancel event object
51704          */
51705         "beforeremove" : true,
51706         /**
51707          * @event invalidated
51708          * Fires when the layout for this region is changed.
51709          * @param {Roo.LayoutRegion} this
51710          */
51711         "invalidated" : true,
51712         /**
51713          * @event visibilitychange
51714          * Fires when this region is shown or hidden 
51715          * @param {Roo.LayoutRegion} this
51716          * @param {Boolean} visibility true or false
51717          */
51718         "visibilitychange" : true,
51719         /**
51720          * @event paneladded
51721          * Fires when a panel is added. 
51722          * @param {Roo.LayoutRegion} this
51723          * @param {Roo.ContentPanel} panel The panel
51724          */
51725         "paneladded" : true,
51726         /**
51727          * @event panelremoved
51728          * Fires when a panel is removed. 
51729          * @param {Roo.LayoutRegion} this
51730          * @param {Roo.ContentPanel} panel The panel
51731          */
51732         "panelremoved" : true,
51733         /**
51734          * @event beforecollapse
51735          * Fires when this region before collapse.
51736          * @param {Roo.LayoutRegion} this
51737          */
51738         "beforecollapse" : true,
51739         /**
51740          * @event collapsed
51741          * Fires when this region is collapsed.
51742          * @param {Roo.LayoutRegion} this
51743          */
51744         "collapsed" : true,
51745         /**
51746          * @event expanded
51747          * Fires when this region is expanded.
51748          * @param {Roo.LayoutRegion} this
51749          */
51750         "expanded" : true,
51751         /**
51752          * @event slideshow
51753          * Fires when this region is slid into view.
51754          * @param {Roo.LayoutRegion} this
51755          */
51756         "slideshow" : true,
51757         /**
51758          * @event slidehide
51759          * Fires when this region slides out of view. 
51760          * @param {Roo.LayoutRegion} this
51761          */
51762         "slidehide" : true,
51763         /**
51764          * @event panelactivated
51765          * Fires when a panel is activated. 
51766          * @param {Roo.LayoutRegion} this
51767          * @param {Roo.ContentPanel} panel The activated panel
51768          */
51769         "panelactivated" : true,
51770         /**
51771          * @event resized
51772          * Fires when the user resizes this region. 
51773          * @param {Roo.LayoutRegion} this
51774          * @param {Number} newSize The new size (width for east/west, height for north/south)
51775          */
51776         "resized" : true
51777     };
51778     /** A collection of panels in this region. @type Roo.util.MixedCollection */
51779     this.panels = new Roo.util.MixedCollection();
51780     this.panels.getKey = this.getPanelId.createDelegate(this);
51781     this.box = null;
51782     this.activePanel = null;
51783     // ensure listeners are added...
51784     
51785     if (config.listeners || config.events) {
51786         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
51787             listeners : config.listeners || {},
51788             events : config.events || {}
51789         });
51790     }
51791     
51792     if(skipConfig !== true){
51793         this.applyConfig(config);
51794     }
51795 };
51796
51797 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
51798     getPanelId : function(p){
51799         return p.getId();
51800     },
51801     
51802     applyConfig : function(config){
51803         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51804         this.config = config;
51805         
51806     },
51807     
51808     /**
51809      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
51810      * the width, for horizontal (north, south) the height.
51811      * @param {Number} newSize The new width or height
51812      */
51813     resizeTo : function(newSize){
51814         var el = this.el ? this.el :
51815                  (this.activePanel ? this.activePanel.getEl() : null);
51816         if(el){
51817             switch(this.position){
51818                 case "east":
51819                 case "west":
51820                     el.setWidth(newSize);
51821                     this.fireEvent("resized", this, newSize);
51822                 break;
51823                 case "north":
51824                 case "south":
51825                     el.setHeight(newSize);
51826                     this.fireEvent("resized", this, newSize);
51827                 break;                
51828             }
51829         }
51830     },
51831     
51832     getBox : function(){
51833         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
51834     },
51835     
51836     getMargins : function(){
51837         return this.margins;
51838     },
51839     
51840     updateBox : function(box){
51841         this.box = box;
51842         var el = this.activePanel.getEl();
51843         el.dom.style.left = box.x + "px";
51844         el.dom.style.top = box.y + "px";
51845         this.activePanel.setSize(box.width, box.height);
51846     },
51847     
51848     /**
51849      * Returns the container element for this region.
51850      * @return {Roo.Element}
51851      */
51852     getEl : function(){
51853         return this.activePanel;
51854     },
51855     
51856     /**
51857      * Returns true if this region is currently visible.
51858      * @return {Boolean}
51859      */
51860     isVisible : function(){
51861         return this.activePanel ? true : false;
51862     },
51863     
51864     setActivePanel : function(panel){
51865         panel = this.getPanel(panel);
51866         if(this.activePanel && this.activePanel != panel){
51867             this.activePanel.setActiveState(false);
51868             this.activePanel.getEl().setLeftTop(-10000,-10000);
51869         }
51870         this.activePanel = panel;
51871         panel.setActiveState(true);
51872         if(this.box){
51873             panel.setSize(this.box.width, this.box.height);
51874         }
51875         this.fireEvent("panelactivated", this, panel);
51876         this.fireEvent("invalidated");
51877     },
51878     
51879     /**
51880      * Show the specified panel.
51881      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
51882      * @return {Roo.ContentPanel} The shown panel or null
51883      */
51884     showPanel : function(panel){
51885         if(panel = this.getPanel(panel)){
51886             this.setActivePanel(panel);
51887         }
51888         return panel;
51889     },
51890     
51891     /**
51892      * Get the active panel for this region.
51893      * @return {Roo.ContentPanel} The active panel or null
51894      */
51895     getActivePanel : function(){
51896         return this.activePanel;
51897     },
51898     
51899     /**
51900      * Add the passed ContentPanel(s)
51901      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
51902      * @return {Roo.ContentPanel} The panel added (if only one was added)
51903      */
51904     add : function(panel){
51905         if(arguments.length > 1){
51906             for(var i = 0, len = arguments.length; i < len; i++) {
51907                 this.add(arguments[i]);
51908             }
51909             return null;
51910         }
51911         if(this.hasPanel(panel)){
51912             this.showPanel(panel);
51913             return panel;
51914         }
51915         var el = panel.getEl();
51916         if(el.dom.parentNode != this.mgr.el.dom){
51917             this.mgr.el.dom.appendChild(el.dom);
51918         }
51919         if(panel.setRegion){
51920             panel.setRegion(this);
51921         }
51922         this.panels.add(panel);
51923         el.setStyle("position", "absolute");
51924         if(!panel.background){
51925             this.setActivePanel(panel);
51926             if(this.config.initialSize && this.panels.getCount()==1){
51927                 this.resizeTo(this.config.initialSize);
51928             }
51929         }
51930         this.fireEvent("paneladded", this, panel);
51931         return panel;
51932     },
51933     
51934     /**
51935      * Returns true if the panel is in this region.
51936      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51937      * @return {Boolean}
51938      */
51939     hasPanel : function(panel){
51940         if(typeof panel == "object"){ // must be panel obj
51941             panel = panel.getId();
51942         }
51943         return this.getPanel(panel) ? true : false;
51944     },
51945     
51946     /**
51947      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
51948      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51949      * @param {Boolean} preservePanel Overrides the config preservePanel option
51950      * @return {Roo.ContentPanel} The panel that was removed
51951      */
51952     remove : function(panel, preservePanel){
51953         panel = this.getPanel(panel);
51954         if(!panel){
51955             return null;
51956         }
51957         var e = {};
51958         this.fireEvent("beforeremove", this, panel, e);
51959         if(e.cancel === true){
51960             return null;
51961         }
51962         var panelId = panel.getId();
51963         this.panels.removeKey(panelId);
51964         return panel;
51965     },
51966     
51967     /**
51968      * Returns the panel specified or null if it's not in this region.
51969      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51970      * @return {Roo.ContentPanel}
51971      */
51972     getPanel : function(id){
51973         if(typeof id == "object"){ // must be panel obj
51974             return id;
51975         }
51976         return this.panels.get(id);
51977     },
51978     
51979     /**
51980      * Returns this regions position (north/south/east/west/center).
51981      * @return {String} 
51982      */
51983     getPosition: function(){
51984         return this.position;    
51985     }
51986 });/*
51987  * Based on:
51988  * Ext JS Library 1.1.1
51989  * Copyright(c) 2006-2007, Ext JS, LLC.
51990  *
51991  * Originally Released Under LGPL - original licence link has changed is not relivant.
51992  *
51993  * Fork - LGPL
51994  * <script type="text/javascript">
51995  */
51996  
51997 /**
51998  * @class Roo.LayoutRegion
51999  * @extends Roo.BasicLayoutRegion
52000  * This class represents a region in a layout manager.
52001  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
52002  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
52003  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
52004  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
52005  * @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})
52006  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
52007  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
52008  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
52009  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
52010  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
52011  * @cfg {String}    title           The title for the region (overrides panel titles)
52012  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
52013  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
52014  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
52015  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
52016  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
52017  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
52018  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
52019  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
52020  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
52021  * @cfg {Boolean}   showPin         True to show a pin button
52022  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
52023  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
52024  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
52025  * @cfg {Number}    width           For East/West panels
52026  * @cfg {Number}    height          For North/South panels
52027  * @cfg {Boolean}   split           To show the splitter
52028  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
52029  */
52030 Roo.LayoutRegion = function(mgr, config, pos){
52031     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
52032     var dh = Roo.DomHelper;
52033     /** This region's container element 
52034     * @type Roo.Element */
52035     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
52036     /** This region's title element 
52037     * @type Roo.Element */
52038
52039     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
52040         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
52041         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
52042     ]}, true);
52043     this.titleEl.enableDisplayMode();
52044     /** This region's title text element 
52045     * @type HTMLElement */
52046     this.titleTextEl = this.titleEl.dom.firstChild;
52047     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
52048     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
52049     this.closeBtn.enableDisplayMode();
52050     this.closeBtn.on("click", this.closeClicked, this);
52051     this.closeBtn.hide();
52052
52053     this.createBody(config);
52054     this.visible = true;
52055     this.collapsed = false;
52056
52057     if(config.hideWhenEmpty){
52058         this.hide();
52059         this.on("paneladded", this.validateVisibility, this);
52060         this.on("panelremoved", this.validateVisibility, this);
52061     }
52062     this.applyConfig(config);
52063 };
52064
52065 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
52066
52067     createBody : function(){
52068         /** This region's body element 
52069         * @type Roo.Element */
52070         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
52071     },
52072
52073     applyConfig : function(c){
52074         if(c.collapsible && this.position != "center" && !this.collapsedEl){
52075             var dh = Roo.DomHelper;
52076             if(c.titlebar !== false){
52077                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
52078                 this.collapseBtn.on("click", this.collapse, this);
52079                 this.collapseBtn.enableDisplayMode();
52080
52081                 if(c.showPin === true || this.showPin){
52082                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
52083                     this.stickBtn.enableDisplayMode();
52084                     this.stickBtn.on("click", this.expand, this);
52085                     this.stickBtn.hide();
52086                 }
52087             }
52088             /** This region's collapsed element
52089             * @type Roo.Element */
52090             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
52091                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
52092             ]}, true);
52093             if(c.floatable !== false){
52094                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
52095                this.collapsedEl.on("click", this.collapseClick, this);
52096             }
52097
52098             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
52099                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
52100                    id: "message", unselectable: "on", style:{"float":"left"}});
52101                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
52102              }
52103             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
52104             this.expandBtn.on("click", this.expand, this);
52105         }
52106         if(this.collapseBtn){
52107             this.collapseBtn.setVisible(c.collapsible == true);
52108         }
52109         this.cmargins = c.cmargins || this.cmargins ||
52110                          (this.position == "west" || this.position == "east" ?
52111                              {top: 0, left: 2, right:2, bottom: 0} :
52112                              {top: 2, left: 0, right:0, bottom: 2});
52113         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52114         this.bottomTabs = c.tabPosition != "top";
52115         this.autoScroll = c.autoScroll || false;
52116         if(this.autoScroll){
52117             this.bodyEl.setStyle("overflow", "auto");
52118         }else{
52119             this.bodyEl.setStyle("overflow", "hidden");
52120         }
52121         //if(c.titlebar !== false){
52122             if((!c.titlebar && !c.title) || c.titlebar === false){
52123                 this.titleEl.hide();
52124             }else{
52125                 this.titleEl.show();
52126                 if(c.title){
52127                     this.titleTextEl.innerHTML = c.title;
52128                 }
52129             }
52130         //}
52131         this.duration = c.duration || .30;
52132         this.slideDuration = c.slideDuration || .45;
52133         this.config = c;
52134         if(c.collapsed){
52135             this.collapse(true);
52136         }
52137         if(c.hidden){
52138             this.hide();
52139         }
52140     },
52141     /**
52142      * Returns true if this region is currently visible.
52143      * @return {Boolean}
52144      */
52145     isVisible : function(){
52146         return this.visible;
52147     },
52148
52149     /**
52150      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
52151      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
52152      */
52153     setCollapsedTitle : function(title){
52154         title = title || "&#160;";
52155         if(this.collapsedTitleTextEl){
52156             this.collapsedTitleTextEl.innerHTML = title;
52157         }
52158     },
52159
52160     getBox : function(){
52161         var b;
52162         if(!this.collapsed){
52163             b = this.el.getBox(false, true);
52164         }else{
52165             b = this.collapsedEl.getBox(false, true);
52166         }
52167         return b;
52168     },
52169
52170     getMargins : function(){
52171         return this.collapsed ? this.cmargins : this.margins;
52172     },
52173
52174     highlight : function(){
52175         this.el.addClass("x-layout-panel-dragover");
52176     },
52177
52178     unhighlight : function(){
52179         this.el.removeClass("x-layout-panel-dragover");
52180     },
52181
52182     updateBox : function(box){
52183         this.box = box;
52184         if(!this.collapsed){
52185             this.el.dom.style.left = box.x + "px";
52186             this.el.dom.style.top = box.y + "px";
52187             this.updateBody(box.width, box.height);
52188         }else{
52189             this.collapsedEl.dom.style.left = box.x + "px";
52190             this.collapsedEl.dom.style.top = box.y + "px";
52191             this.collapsedEl.setSize(box.width, box.height);
52192         }
52193         if(this.tabs){
52194             this.tabs.autoSizeTabs();
52195         }
52196     },
52197
52198     updateBody : function(w, h){
52199         if(w !== null){
52200             this.el.setWidth(w);
52201             w -= this.el.getBorderWidth("rl");
52202             if(this.config.adjustments){
52203                 w += this.config.adjustments[0];
52204             }
52205         }
52206         if(h !== null){
52207             this.el.setHeight(h);
52208             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52209             h -= this.el.getBorderWidth("tb");
52210             if(this.config.adjustments){
52211                 h += this.config.adjustments[1];
52212             }
52213             this.bodyEl.setHeight(h);
52214             if(this.tabs){
52215                 h = this.tabs.syncHeight(h);
52216             }
52217         }
52218         if(this.panelSize){
52219             w = w !== null ? w : this.panelSize.width;
52220             h = h !== null ? h : this.panelSize.height;
52221         }
52222         if(this.activePanel){
52223             var el = this.activePanel.getEl();
52224             w = w !== null ? w : el.getWidth();
52225             h = h !== null ? h : el.getHeight();
52226             this.panelSize = {width: w, height: h};
52227             this.activePanel.setSize(w, h);
52228         }
52229         if(Roo.isIE && this.tabs){
52230             this.tabs.el.repaint();
52231         }
52232     },
52233
52234     /**
52235      * Returns the container element for this region.
52236      * @return {Roo.Element}
52237      */
52238     getEl : function(){
52239         return this.el;
52240     },
52241
52242     /**
52243      * Hides this region.
52244      */
52245     hide : function(){
52246         if(!this.collapsed){
52247             this.el.dom.style.left = "-2000px";
52248             this.el.hide();
52249         }else{
52250             this.collapsedEl.dom.style.left = "-2000px";
52251             this.collapsedEl.hide();
52252         }
52253         this.visible = false;
52254         this.fireEvent("visibilitychange", this, false);
52255     },
52256
52257     /**
52258      * Shows this region if it was previously hidden.
52259      */
52260     show : function(){
52261         if(!this.collapsed){
52262             this.el.show();
52263         }else{
52264             this.collapsedEl.show();
52265         }
52266         this.visible = true;
52267         this.fireEvent("visibilitychange", this, true);
52268     },
52269
52270     closeClicked : function(){
52271         if(this.activePanel){
52272             this.remove(this.activePanel);
52273         }
52274     },
52275
52276     collapseClick : function(e){
52277         if(this.isSlid){
52278            e.stopPropagation();
52279            this.slideIn();
52280         }else{
52281            e.stopPropagation();
52282            this.slideOut();
52283         }
52284     },
52285
52286     /**
52287      * Collapses this region.
52288      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52289      */
52290     collapse : function(skipAnim, skipCheck = false){
52291         if(this.collapsed) {
52292             return;
52293         }
52294         
52295         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52296             
52297             this.collapsed = true;
52298             if(this.split){
52299                 this.split.el.hide();
52300             }
52301             if(this.config.animate && skipAnim !== true){
52302                 this.fireEvent("invalidated", this);
52303                 this.animateCollapse();
52304             }else{
52305                 this.el.setLocation(-20000,-20000);
52306                 this.el.hide();
52307                 this.collapsedEl.show();
52308                 this.fireEvent("collapsed", this);
52309                 this.fireEvent("invalidated", this);
52310             }
52311         }
52312         
52313     },
52314
52315     animateCollapse : function(){
52316         // overridden
52317     },
52318
52319     /**
52320      * Expands this region if it was previously collapsed.
52321      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52322      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52323      */
52324     expand : function(e, skipAnim){
52325         if(e) {
52326             e.stopPropagation();
52327         }
52328         if(!this.collapsed || this.el.hasActiveFx()) {
52329             return;
52330         }
52331         if(this.isSlid){
52332             this.afterSlideIn();
52333             skipAnim = true;
52334         }
52335         this.collapsed = false;
52336         if(this.config.animate && skipAnim !== true){
52337             this.animateExpand();
52338         }else{
52339             this.el.show();
52340             if(this.split){
52341                 this.split.el.show();
52342             }
52343             this.collapsedEl.setLocation(-2000,-2000);
52344             this.collapsedEl.hide();
52345             this.fireEvent("invalidated", this);
52346             this.fireEvent("expanded", this);
52347         }
52348     },
52349
52350     animateExpand : function(){
52351         // overridden
52352     },
52353
52354     initTabs : function()
52355     {
52356         this.bodyEl.setStyle("overflow", "hidden");
52357         var ts = new Roo.TabPanel(
52358                 this.bodyEl.dom,
52359                 {
52360                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52361                     disableTooltips: this.config.disableTabTips,
52362                     toolbar : this.config.toolbar
52363                 }
52364         );
52365         if(this.config.hideTabs){
52366             ts.stripWrap.setDisplayed(false);
52367         }
52368         this.tabs = ts;
52369         ts.resizeTabs = this.config.resizeTabs === true;
52370         ts.minTabWidth = this.config.minTabWidth || 40;
52371         ts.maxTabWidth = this.config.maxTabWidth || 250;
52372         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52373         ts.monitorResize = false;
52374         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52375         ts.bodyEl.addClass('x-layout-tabs-body');
52376         this.panels.each(this.initPanelAsTab, this);
52377     },
52378
52379     initPanelAsTab : function(panel){
52380         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52381                     this.config.closeOnTab && panel.isClosable());
52382         if(panel.tabTip !== undefined){
52383             ti.setTooltip(panel.tabTip);
52384         }
52385         ti.on("activate", function(){
52386               this.setActivePanel(panel);
52387         }, this);
52388         if(this.config.closeOnTab){
52389             ti.on("beforeclose", function(t, e){
52390                 e.cancel = true;
52391                 this.remove(panel);
52392             }, this);
52393         }
52394         return ti;
52395     },
52396
52397     updatePanelTitle : function(panel, title){
52398         if(this.activePanel == panel){
52399             this.updateTitle(title);
52400         }
52401         if(this.tabs){
52402             var ti = this.tabs.getTab(panel.getEl().id);
52403             ti.setText(title);
52404             if(panel.tabTip !== undefined){
52405                 ti.setTooltip(panel.tabTip);
52406             }
52407         }
52408     },
52409
52410     updateTitle : function(title){
52411         if(this.titleTextEl && !this.config.title){
52412             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52413         }
52414     },
52415
52416     setActivePanel : function(panel){
52417         panel = this.getPanel(panel);
52418         if(this.activePanel && this.activePanel != panel){
52419             this.activePanel.setActiveState(false);
52420         }
52421         this.activePanel = panel;
52422         panel.setActiveState(true);
52423         if(this.panelSize){
52424             panel.setSize(this.panelSize.width, this.panelSize.height);
52425         }
52426         if(this.closeBtn){
52427             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52428         }
52429         this.updateTitle(panel.getTitle());
52430         if(this.tabs){
52431             this.fireEvent("invalidated", this);
52432         }
52433         this.fireEvent("panelactivated", this, panel);
52434     },
52435
52436     /**
52437      * Shows the specified panel.
52438      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52439      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52440      */
52441     showPanel : function(panel)
52442     {
52443         panel = this.getPanel(panel);
52444         if(panel){
52445             if(this.tabs){
52446                 var tab = this.tabs.getTab(panel.getEl().id);
52447                 if(tab.isHidden()){
52448                     this.tabs.unhideTab(tab.id);
52449                 }
52450                 tab.activate();
52451             }else{
52452                 this.setActivePanel(panel);
52453             }
52454         }
52455         return panel;
52456     },
52457
52458     /**
52459      * Get the active panel for this region.
52460      * @return {Roo.ContentPanel} The active panel or null
52461      */
52462     getActivePanel : function(){
52463         return this.activePanel;
52464     },
52465
52466     validateVisibility : function(){
52467         if(this.panels.getCount() < 1){
52468             this.updateTitle("&#160;");
52469             this.closeBtn.hide();
52470             this.hide();
52471         }else{
52472             if(!this.isVisible()){
52473                 this.show();
52474             }
52475         }
52476     },
52477
52478     /**
52479      * Adds the passed ContentPanel(s) to this region.
52480      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52481      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52482      */
52483     add : function(panel){
52484         if(arguments.length > 1){
52485             for(var i = 0, len = arguments.length; i < len; i++) {
52486                 this.add(arguments[i]);
52487             }
52488             return null;
52489         }
52490         if(this.hasPanel(panel)){
52491             this.showPanel(panel);
52492             return panel;
52493         }
52494         panel.setRegion(this);
52495         this.panels.add(panel);
52496         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52497             this.bodyEl.dom.appendChild(panel.getEl().dom);
52498             if(panel.background !== true){
52499                 this.setActivePanel(panel);
52500             }
52501             this.fireEvent("paneladded", this, panel);
52502             return panel;
52503         }
52504         if(!this.tabs){
52505             this.initTabs();
52506         }else{
52507             this.initPanelAsTab(panel);
52508         }
52509         if(panel.background !== true){
52510             this.tabs.activate(panel.getEl().id);
52511         }
52512         this.fireEvent("paneladded", this, panel);
52513         return panel;
52514     },
52515
52516     /**
52517      * Hides the tab for the specified panel.
52518      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52519      */
52520     hidePanel : function(panel){
52521         if(this.tabs && (panel = this.getPanel(panel))){
52522             this.tabs.hideTab(panel.getEl().id);
52523         }
52524     },
52525
52526     /**
52527      * Unhides the tab for a previously hidden panel.
52528      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52529      */
52530     unhidePanel : function(panel){
52531         if(this.tabs && (panel = this.getPanel(panel))){
52532             this.tabs.unhideTab(panel.getEl().id);
52533         }
52534     },
52535
52536     clearPanels : function(){
52537         while(this.panels.getCount() > 0){
52538              this.remove(this.panels.first());
52539         }
52540     },
52541
52542     /**
52543      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52544      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52545      * @param {Boolean} preservePanel Overrides the config preservePanel option
52546      * @return {Roo.ContentPanel} The panel that was removed
52547      */
52548     remove : function(panel, preservePanel){
52549         panel = this.getPanel(panel);
52550         if(!panel){
52551             return null;
52552         }
52553         var e = {};
52554         this.fireEvent("beforeremove", this, panel, e);
52555         if(e.cancel === true){
52556             return null;
52557         }
52558         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52559         var panelId = panel.getId();
52560         this.panels.removeKey(panelId);
52561         if(preservePanel){
52562             document.body.appendChild(panel.getEl().dom);
52563         }
52564         if(this.tabs){
52565             this.tabs.removeTab(panel.getEl().id);
52566         }else if (!preservePanel){
52567             this.bodyEl.dom.removeChild(panel.getEl().dom);
52568         }
52569         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52570             var p = this.panels.first();
52571             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52572             tempEl.appendChild(p.getEl().dom);
52573             this.bodyEl.update("");
52574             this.bodyEl.dom.appendChild(p.getEl().dom);
52575             tempEl = null;
52576             this.updateTitle(p.getTitle());
52577             this.tabs = null;
52578             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52579             this.setActivePanel(p);
52580         }
52581         panel.setRegion(null);
52582         if(this.activePanel == panel){
52583             this.activePanel = null;
52584         }
52585         if(this.config.autoDestroy !== false && preservePanel !== true){
52586             try{panel.destroy();}catch(e){}
52587         }
52588         this.fireEvent("panelremoved", this, panel);
52589         return panel;
52590     },
52591
52592     /**
52593      * Returns the TabPanel component used by this region
52594      * @return {Roo.TabPanel}
52595      */
52596     getTabs : function(){
52597         return this.tabs;
52598     },
52599
52600     createTool : function(parentEl, className){
52601         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52602             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52603         btn.addClassOnOver("x-layout-tools-button-over");
52604         return btn;
52605     }
52606 });/*
52607  * Based on:
52608  * Ext JS Library 1.1.1
52609  * Copyright(c) 2006-2007, Ext JS, LLC.
52610  *
52611  * Originally Released Under LGPL - original licence link has changed is not relivant.
52612  *
52613  * Fork - LGPL
52614  * <script type="text/javascript">
52615  */
52616  
52617
52618
52619 /**
52620  * @class Roo.SplitLayoutRegion
52621  * @extends Roo.LayoutRegion
52622  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52623  */
52624 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52625     this.cursor = cursor;
52626     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52627 };
52628
52629 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52630     splitTip : "Drag to resize.",
52631     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52632     useSplitTips : false,
52633
52634     applyConfig : function(config){
52635         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52636         if(config.split){
52637             if(!this.split){
52638                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52639                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52640                 /** The SplitBar for this region 
52641                 * @type Roo.SplitBar */
52642                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52643                 this.split.on("moved", this.onSplitMove, this);
52644                 this.split.useShim = config.useShim === true;
52645                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
52646                 if(this.useSplitTips){
52647                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
52648                 }
52649                 if(config.collapsible){
52650                     this.split.el.on("dblclick", this.collapse,  this);
52651                 }
52652             }
52653             if(typeof config.minSize != "undefined"){
52654                 this.split.minSize = config.minSize;
52655             }
52656             if(typeof config.maxSize != "undefined"){
52657                 this.split.maxSize = config.maxSize;
52658             }
52659             if(config.hideWhenEmpty || config.hidden || config.collapsed){
52660                 this.hideSplitter();
52661             }
52662         }
52663     },
52664
52665     getHMaxSize : function(){
52666          var cmax = this.config.maxSize || 10000;
52667          var center = this.mgr.getRegion("center");
52668          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
52669     },
52670
52671     getVMaxSize : function(){
52672          var cmax = this.config.maxSize || 10000;
52673          var center = this.mgr.getRegion("center");
52674          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
52675     },
52676
52677     onSplitMove : function(split, newSize){
52678         this.fireEvent("resized", this, newSize);
52679     },
52680     
52681     /** 
52682      * Returns the {@link Roo.SplitBar} for this region.
52683      * @return {Roo.SplitBar}
52684      */
52685     getSplitBar : function(){
52686         return this.split;
52687     },
52688     
52689     hide : function(){
52690         this.hideSplitter();
52691         Roo.SplitLayoutRegion.superclass.hide.call(this);
52692     },
52693
52694     hideSplitter : function(){
52695         if(this.split){
52696             this.split.el.setLocation(-2000,-2000);
52697             this.split.el.hide();
52698         }
52699     },
52700
52701     show : function(){
52702         if(this.split){
52703             this.split.el.show();
52704         }
52705         Roo.SplitLayoutRegion.superclass.show.call(this);
52706     },
52707     
52708     beforeSlide: function(){
52709         if(Roo.isGecko){// firefox overflow auto bug workaround
52710             this.bodyEl.clip();
52711             if(this.tabs) {
52712                 this.tabs.bodyEl.clip();
52713             }
52714             if(this.activePanel){
52715                 this.activePanel.getEl().clip();
52716                 
52717                 if(this.activePanel.beforeSlide){
52718                     this.activePanel.beforeSlide();
52719                 }
52720             }
52721         }
52722     },
52723     
52724     afterSlide : function(){
52725         if(Roo.isGecko){// firefox overflow auto bug workaround
52726             this.bodyEl.unclip();
52727             if(this.tabs) {
52728                 this.tabs.bodyEl.unclip();
52729             }
52730             if(this.activePanel){
52731                 this.activePanel.getEl().unclip();
52732                 if(this.activePanel.afterSlide){
52733                     this.activePanel.afterSlide();
52734                 }
52735             }
52736         }
52737     },
52738
52739     initAutoHide : function(){
52740         if(this.autoHide !== false){
52741             if(!this.autoHideHd){
52742                 var st = new Roo.util.DelayedTask(this.slideIn, this);
52743                 this.autoHideHd = {
52744                     "mouseout": function(e){
52745                         if(!e.within(this.el, true)){
52746                             st.delay(500);
52747                         }
52748                     },
52749                     "mouseover" : function(e){
52750                         st.cancel();
52751                     },
52752                     scope : this
52753                 };
52754             }
52755             this.el.on(this.autoHideHd);
52756         }
52757     },
52758
52759     clearAutoHide : function(){
52760         if(this.autoHide !== false){
52761             this.el.un("mouseout", this.autoHideHd.mouseout);
52762             this.el.un("mouseover", this.autoHideHd.mouseover);
52763         }
52764     },
52765
52766     clearMonitor : function(){
52767         Roo.get(document).un("click", this.slideInIf, this);
52768     },
52769
52770     // these names are backwards but not changed for compat
52771     slideOut : function(){
52772         if(this.isSlid || this.el.hasActiveFx()){
52773             return;
52774         }
52775         this.isSlid = true;
52776         if(this.collapseBtn){
52777             this.collapseBtn.hide();
52778         }
52779         this.closeBtnState = this.closeBtn.getStyle('display');
52780         this.closeBtn.hide();
52781         if(this.stickBtn){
52782             this.stickBtn.show();
52783         }
52784         this.el.show();
52785         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
52786         this.beforeSlide();
52787         this.el.setStyle("z-index", 10001);
52788         this.el.slideIn(this.getSlideAnchor(), {
52789             callback: function(){
52790                 this.afterSlide();
52791                 this.initAutoHide();
52792                 Roo.get(document).on("click", this.slideInIf, this);
52793                 this.fireEvent("slideshow", this);
52794             },
52795             scope: this,
52796             block: true
52797         });
52798     },
52799
52800     afterSlideIn : function(){
52801         this.clearAutoHide();
52802         this.isSlid = false;
52803         this.clearMonitor();
52804         this.el.setStyle("z-index", "");
52805         if(this.collapseBtn){
52806             this.collapseBtn.show();
52807         }
52808         this.closeBtn.setStyle('display', this.closeBtnState);
52809         if(this.stickBtn){
52810             this.stickBtn.hide();
52811         }
52812         this.fireEvent("slidehide", this);
52813     },
52814
52815     slideIn : function(cb){
52816         if(!this.isSlid || this.el.hasActiveFx()){
52817             Roo.callback(cb);
52818             return;
52819         }
52820         this.isSlid = false;
52821         this.beforeSlide();
52822         this.el.slideOut(this.getSlideAnchor(), {
52823             callback: function(){
52824                 this.el.setLeftTop(-10000, -10000);
52825                 this.afterSlide();
52826                 this.afterSlideIn();
52827                 Roo.callback(cb);
52828             },
52829             scope: this,
52830             block: true
52831         });
52832     },
52833     
52834     slideInIf : function(e){
52835         if(!e.within(this.el)){
52836             this.slideIn();
52837         }
52838     },
52839
52840     animateCollapse : function(){
52841         this.beforeSlide();
52842         this.el.setStyle("z-index", 20000);
52843         var anchor = this.getSlideAnchor();
52844         this.el.slideOut(anchor, {
52845             callback : function(){
52846                 this.el.setStyle("z-index", "");
52847                 this.collapsedEl.slideIn(anchor, {duration:.3});
52848                 this.afterSlide();
52849                 this.el.setLocation(-10000,-10000);
52850                 this.el.hide();
52851                 this.fireEvent("collapsed", this);
52852             },
52853             scope: this,
52854             block: true
52855         });
52856     },
52857
52858     animateExpand : function(){
52859         this.beforeSlide();
52860         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
52861         this.el.setStyle("z-index", 20000);
52862         this.collapsedEl.hide({
52863             duration:.1
52864         });
52865         this.el.slideIn(this.getSlideAnchor(), {
52866             callback : function(){
52867                 this.el.setStyle("z-index", "");
52868                 this.afterSlide();
52869                 if(this.split){
52870                     this.split.el.show();
52871                 }
52872                 this.fireEvent("invalidated", this);
52873                 this.fireEvent("expanded", this);
52874             },
52875             scope: this,
52876             block: true
52877         });
52878     },
52879
52880     anchors : {
52881         "west" : "left",
52882         "east" : "right",
52883         "north" : "top",
52884         "south" : "bottom"
52885     },
52886
52887     sanchors : {
52888         "west" : "l",
52889         "east" : "r",
52890         "north" : "t",
52891         "south" : "b"
52892     },
52893
52894     canchors : {
52895         "west" : "tl-tr",
52896         "east" : "tr-tl",
52897         "north" : "tl-bl",
52898         "south" : "bl-tl"
52899     },
52900
52901     getAnchor : function(){
52902         return this.anchors[this.position];
52903     },
52904
52905     getCollapseAnchor : function(){
52906         return this.canchors[this.position];
52907     },
52908
52909     getSlideAnchor : function(){
52910         return this.sanchors[this.position];
52911     },
52912
52913     getAlignAdj : function(){
52914         var cm = this.cmargins;
52915         switch(this.position){
52916             case "west":
52917                 return [0, 0];
52918             break;
52919             case "east":
52920                 return [0, 0];
52921             break;
52922             case "north":
52923                 return [0, 0];
52924             break;
52925             case "south":
52926                 return [0, 0];
52927             break;
52928         }
52929     },
52930
52931     getExpandAdj : function(){
52932         var c = this.collapsedEl, cm = this.cmargins;
52933         switch(this.position){
52934             case "west":
52935                 return [-(cm.right+c.getWidth()+cm.left), 0];
52936             break;
52937             case "east":
52938                 return [cm.right+c.getWidth()+cm.left, 0];
52939             break;
52940             case "north":
52941                 return [0, -(cm.top+cm.bottom+c.getHeight())];
52942             break;
52943             case "south":
52944                 return [0, cm.top+cm.bottom+c.getHeight()];
52945             break;
52946         }
52947     }
52948 });/*
52949  * Based on:
52950  * Ext JS Library 1.1.1
52951  * Copyright(c) 2006-2007, Ext JS, LLC.
52952  *
52953  * Originally Released Under LGPL - original licence link has changed is not relivant.
52954  *
52955  * Fork - LGPL
52956  * <script type="text/javascript">
52957  */
52958 /*
52959  * These classes are private internal classes
52960  */
52961 Roo.CenterLayoutRegion = function(mgr, config){
52962     Roo.LayoutRegion.call(this, mgr, config, "center");
52963     this.visible = true;
52964     this.minWidth = config.minWidth || 20;
52965     this.minHeight = config.minHeight || 20;
52966 };
52967
52968 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
52969     hide : function(){
52970         // center panel can't be hidden
52971     },
52972     
52973     show : function(){
52974         // center panel can't be hidden
52975     },
52976     
52977     getMinWidth: function(){
52978         return this.minWidth;
52979     },
52980     
52981     getMinHeight: function(){
52982         return this.minHeight;
52983     }
52984 });
52985
52986
52987 Roo.NorthLayoutRegion = function(mgr, config){
52988     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
52989     if(this.split){
52990         this.split.placement = Roo.SplitBar.TOP;
52991         this.split.orientation = Roo.SplitBar.VERTICAL;
52992         this.split.el.addClass("x-layout-split-v");
52993     }
52994     var size = config.initialSize || config.height;
52995     if(typeof size != "undefined"){
52996         this.el.setHeight(size);
52997     }
52998 };
52999 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
53000     orientation: Roo.SplitBar.VERTICAL,
53001     getBox : function(){
53002         if(this.collapsed){
53003             return this.collapsedEl.getBox();
53004         }
53005         var box = this.el.getBox();
53006         if(this.split){
53007             box.height += this.split.el.getHeight();
53008         }
53009         return box;
53010     },
53011     
53012     updateBox : function(box){
53013         if(this.split && !this.collapsed){
53014             box.height -= this.split.el.getHeight();
53015             this.split.el.setLeft(box.x);
53016             this.split.el.setTop(box.y+box.height);
53017             this.split.el.setWidth(box.width);
53018         }
53019         if(this.collapsed){
53020             this.updateBody(box.width, null);
53021         }
53022         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53023     }
53024 });
53025
53026 Roo.SouthLayoutRegion = function(mgr, config){
53027     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
53028     if(this.split){
53029         this.split.placement = Roo.SplitBar.BOTTOM;
53030         this.split.orientation = Roo.SplitBar.VERTICAL;
53031         this.split.el.addClass("x-layout-split-v");
53032     }
53033     var size = config.initialSize || config.height;
53034     if(typeof size != "undefined"){
53035         this.el.setHeight(size);
53036     }
53037 };
53038 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
53039     orientation: Roo.SplitBar.VERTICAL,
53040     getBox : function(){
53041         if(this.collapsed){
53042             return this.collapsedEl.getBox();
53043         }
53044         var box = this.el.getBox();
53045         if(this.split){
53046             var sh = this.split.el.getHeight();
53047             box.height += sh;
53048             box.y -= sh;
53049         }
53050         return box;
53051     },
53052     
53053     updateBox : function(box){
53054         if(this.split && !this.collapsed){
53055             var sh = this.split.el.getHeight();
53056             box.height -= sh;
53057             box.y += sh;
53058             this.split.el.setLeft(box.x);
53059             this.split.el.setTop(box.y-sh);
53060             this.split.el.setWidth(box.width);
53061         }
53062         if(this.collapsed){
53063             this.updateBody(box.width, null);
53064         }
53065         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53066     }
53067 });
53068
53069 Roo.EastLayoutRegion = function(mgr, config){
53070     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
53071     if(this.split){
53072         this.split.placement = Roo.SplitBar.RIGHT;
53073         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53074         this.split.el.addClass("x-layout-split-h");
53075     }
53076     var size = config.initialSize || config.width;
53077     if(typeof size != "undefined"){
53078         this.el.setWidth(size);
53079     }
53080 };
53081 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
53082     orientation: Roo.SplitBar.HORIZONTAL,
53083     getBox : function(){
53084         if(this.collapsed){
53085             return this.collapsedEl.getBox();
53086         }
53087         var box = this.el.getBox();
53088         if(this.split){
53089             var sw = this.split.el.getWidth();
53090             box.width += sw;
53091             box.x -= sw;
53092         }
53093         return box;
53094     },
53095
53096     updateBox : function(box){
53097         if(this.split && !this.collapsed){
53098             var sw = this.split.el.getWidth();
53099             box.width -= sw;
53100             this.split.el.setLeft(box.x);
53101             this.split.el.setTop(box.y);
53102             this.split.el.setHeight(box.height);
53103             box.x += sw;
53104         }
53105         if(this.collapsed){
53106             this.updateBody(null, box.height);
53107         }
53108         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53109     }
53110 });
53111
53112 Roo.WestLayoutRegion = function(mgr, config){
53113     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
53114     if(this.split){
53115         this.split.placement = Roo.SplitBar.LEFT;
53116         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53117         this.split.el.addClass("x-layout-split-h");
53118     }
53119     var size = config.initialSize || config.width;
53120     if(typeof size != "undefined"){
53121         this.el.setWidth(size);
53122     }
53123 };
53124 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
53125     orientation: Roo.SplitBar.HORIZONTAL,
53126     getBox : function(){
53127         if(this.collapsed){
53128             return this.collapsedEl.getBox();
53129         }
53130         var box = this.el.getBox();
53131         if(this.split){
53132             box.width += this.split.el.getWidth();
53133         }
53134         return box;
53135     },
53136     
53137     updateBox : function(box){
53138         if(this.split && !this.collapsed){
53139             var sw = this.split.el.getWidth();
53140             box.width -= sw;
53141             this.split.el.setLeft(box.x+box.width);
53142             this.split.el.setTop(box.y);
53143             this.split.el.setHeight(box.height);
53144         }
53145         if(this.collapsed){
53146             this.updateBody(null, box.height);
53147         }
53148         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53149     }
53150 });
53151 /*
53152  * Based on:
53153  * Ext JS Library 1.1.1
53154  * Copyright(c) 2006-2007, Ext JS, LLC.
53155  *
53156  * Originally Released Under LGPL - original licence link has changed is not relivant.
53157  *
53158  * Fork - LGPL
53159  * <script type="text/javascript">
53160  */
53161  
53162  
53163 /*
53164  * Private internal class for reading and applying state
53165  */
53166 Roo.LayoutStateManager = function(layout){
53167      // default empty state
53168      this.state = {
53169         north: {},
53170         south: {},
53171         east: {},
53172         west: {}       
53173     };
53174 };
53175
53176 Roo.LayoutStateManager.prototype = {
53177     init : function(layout, provider){
53178         this.provider = provider;
53179         var state = provider.get(layout.id+"-layout-state");
53180         if(state){
53181             var wasUpdating = layout.isUpdating();
53182             if(!wasUpdating){
53183                 layout.beginUpdate();
53184             }
53185             for(var key in state){
53186                 if(typeof state[key] != "function"){
53187                     var rstate = state[key];
53188                     var r = layout.getRegion(key);
53189                     if(r && rstate){
53190                         if(rstate.size){
53191                             r.resizeTo(rstate.size);
53192                         }
53193                         if(rstate.collapsed == true){
53194                             r.collapse(true);
53195                         }else{
53196                             r.expand(null, true);
53197                         }
53198                     }
53199                 }
53200             }
53201             if(!wasUpdating){
53202                 layout.endUpdate();
53203             }
53204             this.state = state; 
53205         }
53206         this.layout = layout;
53207         layout.on("regionresized", this.onRegionResized, this);
53208         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53209         layout.on("regionexpanded", this.onRegionExpanded, this);
53210     },
53211     
53212     storeState : function(){
53213         this.provider.set(this.layout.id+"-layout-state", this.state);
53214     },
53215     
53216     onRegionResized : function(region, newSize){
53217         this.state[region.getPosition()].size = newSize;
53218         this.storeState();
53219     },
53220     
53221     onRegionCollapsed : function(region){
53222         this.state[region.getPosition()].collapsed = true;
53223         this.storeState();
53224     },
53225     
53226     onRegionExpanded : function(region){
53227         this.state[region.getPosition()].collapsed = false;
53228         this.storeState();
53229     }
53230 };/*
53231  * Based on:
53232  * Ext JS Library 1.1.1
53233  * Copyright(c) 2006-2007, Ext JS, LLC.
53234  *
53235  * Originally Released Under LGPL - original licence link has changed is not relivant.
53236  *
53237  * Fork - LGPL
53238  * <script type="text/javascript">
53239  */
53240 /**
53241  * @class Roo.ContentPanel
53242  * @extends Roo.util.Observable
53243  * A basic ContentPanel element.
53244  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53245  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53246  * @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
53247  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53248  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53249  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53250  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53251  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53252  * @cfg {String} title          The title for this panel
53253  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53254  * @cfg {String} url            Calls {@link #setUrl} with this value
53255  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53256  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53257  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53258  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53259
53260  * @constructor
53261  * Create a new ContentPanel.
53262  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53263  * @param {String/Object} config A string to set only the title or a config object
53264  * @param {String} content (optional) Set the HTML content for this panel
53265  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53266  */
53267 Roo.ContentPanel = function(el, config, content){
53268     
53269      
53270     /*
53271     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53272         config = el;
53273         el = Roo.id();
53274     }
53275     if (config && config.parentLayout) { 
53276         el = config.parentLayout.el.createChild(); 
53277     }
53278     */
53279     if(el.autoCreate){ // xtype is available if this is called from factory
53280         config = el;
53281         el = Roo.id();
53282     }
53283     this.el = Roo.get(el);
53284     if(!this.el && config && config.autoCreate){
53285         if(typeof config.autoCreate == "object"){
53286             if(!config.autoCreate.id){
53287                 config.autoCreate.id = config.id||el;
53288             }
53289             this.el = Roo.DomHelper.append(document.body,
53290                         config.autoCreate, true);
53291         }else{
53292             this.el = Roo.DomHelper.append(document.body,
53293                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53294         }
53295     }
53296     this.closable = false;
53297     this.loaded = false;
53298     this.active = false;
53299     if(typeof config == "string"){
53300         this.title = config;
53301     }else{
53302         Roo.apply(this, config);
53303     }
53304     
53305     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53306         this.wrapEl = this.el.wrap();
53307         this.toolbar.container = this.el.insertSibling(false, 'before');
53308         this.toolbar = new Roo.Toolbar(this.toolbar);
53309     }
53310     
53311     // xtype created footer. - not sure if will work as we normally have to render first..
53312     if (this.footer && !this.footer.el && this.footer.xtype) {
53313         if (!this.wrapEl) {
53314             this.wrapEl = this.el.wrap();
53315         }
53316     
53317         this.footer.container = this.wrapEl.createChild();
53318          
53319         this.footer = Roo.factory(this.footer, Roo);
53320         
53321     }
53322     
53323     if(this.resizeEl){
53324         this.resizeEl = Roo.get(this.resizeEl, true);
53325     }else{
53326         this.resizeEl = this.el;
53327     }
53328     // handle view.xtype
53329     
53330  
53331     
53332     
53333     this.addEvents({
53334         /**
53335          * @event activate
53336          * Fires when this panel is activated. 
53337          * @param {Roo.ContentPanel} this
53338          */
53339         "activate" : true,
53340         /**
53341          * @event deactivate
53342          * Fires when this panel is activated. 
53343          * @param {Roo.ContentPanel} this
53344          */
53345         "deactivate" : true,
53346
53347         /**
53348          * @event resize
53349          * Fires when this panel is resized if fitToFrame is true.
53350          * @param {Roo.ContentPanel} this
53351          * @param {Number} width The width after any component adjustments
53352          * @param {Number} height The height after any component adjustments
53353          */
53354         "resize" : true,
53355         
53356          /**
53357          * @event render
53358          * Fires when this tab is created
53359          * @param {Roo.ContentPanel} this
53360          */
53361         "render" : true
53362         
53363         
53364         
53365     });
53366     
53367
53368     
53369     
53370     if(this.autoScroll){
53371         this.resizeEl.setStyle("overflow", "auto");
53372     } else {
53373         // fix randome scrolling
53374         this.el.on('scroll', function() {
53375             Roo.log('fix random scolling');
53376             this.scrollTo('top',0); 
53377         });
53378     }
53379     content = content || this.content;
53380     if(content){
53381         this.setContent(content);
53382     }
53383     if(config && config.url){
53384         this.setUrl(this.url, this.params, this.loadOnce);
53385     }
53386     
53387     
53388     
53389     Roo.ContentPanel.superclass.constructor.call(this);
53390     
53391     if (this.view && typeof(this.view.xtype) != 'undefined') {
53392         this.view.el = this.el.appendChild(document.createElement("div"));
53393         this.view = Roo.factory(this.view); 
53394         this.view.render  &&  this.view.render(false, '');  
53395     }
53396     
53397     
53398     this.fireEvent('render', this);
53399 };
53400
53401 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53402     tabTip:'',
53403     setRegion : function(region){
53404         this.region = region;
53405         if(region){
53406            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53407         }else{
53408            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53409         } 
53410     },
53411     
53412     /**
53413      * Returns the toolbar for this Panel if one was configured. 
53414      * @return {Roo.Toolbar} 
53415      */
53416     getToolbar : function(){
53417         return this.toolbar;
53418     },
53419     
53420     setActiveState : function(active){
53421         this.active = active;
53422         if(!active){
53423             this.fireEvent("deactivate", this);
53424         }else{
53425             this.fireEvent("activate", this);
53426         }
53427     },
53428     /**
53429      * Updates this panel's element
53430      * @param {String} content The new content
53431      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53432     */
53433     setContent : function(content, loadScripts){
53434         this.el.update(content, loadScripts);
53435     },
53436
53437     ignoreResize : function(w, h){
53438         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53439             return true;
53440         }else{
53441             this.lastSize = {width: w, height: h};
53442             return false;
53443         }
53444     },
53445     /**
53446      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53447      * @return {Roo.UpdateManager} The UpdateManager
53448      */
53449     getUpdateManager : function(){
53450         return this.el.getUpdateManager();
53451     },
53452      /**
53453      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53454      * @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:
53455 <pre><code>
53456 panel.load({
53457     url: "your-url.php",
53458     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53459     callback: yourFunction,
53460     scope: yourObject, //(optional scope)
53461     discardUrl: false,
53462     nocache: false,
53463     text: "Loading...",
53464     timeout: 30,
53465     scripts: false
53466 });
53467 </code></pre>
53468      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53469      * 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.
53470      * @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}
53471      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53472      * @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.
53473      * @return {Roo.ContentPanel} this
53474      */
53475     load : function(){
53476         var um = this.el.getUpdateManager();
53477         um.update.apply(um, arguments);
53478         return this;
53479     },
53480
53481
53482     /**
53483      * 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.
53484      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53485      * @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)
53486      * @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)
53487      * @return {Roo.UpdateManager} The UpdateManager
53488      */
53489     setUrl : function(url, params, loadOnce){
53490         if(this.refreshDelegate){
53491             this.removeListener("activate", this.refreshDelegate);
53492         }
53493         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53494         this.on("activate", this.refreshDelegate);
53495         return this.el.getUpdateManager();
53496     },
53497     
53498     _handleRefresh : function(url, params, loadOnce){
53499         if(!loadOnce || !this.loaded){
53500             var updater = this.el.getUpdateManager();
53501             updater.update(url, params, this._setLoaded.createDelegate(this));
53502         }
53503     },
53504     
53505     _setLoaded : function(){
53506         this.loaded = true;
53507     }, 
53508     
53509     /**
53510      * Returns this panel's id
53511      * @return {String} 
53512      */
53513     getId : function(){
53514         return this.el.id;
53515     },
53516     
53517     /** 
53518      * Returns this panel's element - used by regiosn to add.
53519      * @return {Roo.Element} 
53520      */
53521     getEl : function(){
53522         return this.wrapEl || this.el;
53523     },
53524     
53525     adjustForComponents : function(width, height)
53526     {
53527         //Roo.log('adjustForComponents ');
53528         if(this.resizeEl != this.el){
53529             width -= this.el.getFrameWidth('lr');
53530             height -= this.el.getFrameWidth('tb');
53531         }
53532         if(this.toolbar){
53533             var te = this.toolbar.getEl();
53534             height -= te.getHeight();
53535             te.setWidth(width);
53536         }
53537         if(this.footer){
53538             var te = this.footer.getEl();
53539             Roo.log("footer:" + te.getHeight());
53540             
53541             height -= te.getHeight();
53542             te.setWidth(width);
53543         }
53544         
53545         
53546         if(this.adjustments){
53547             width += this.adjustments[0];
53548             height += this.adjustments[1];
53549         }
53550         return {"width": width, "height": height};
53551     },
53552     
53553     setSize : function(width, height){
53554         if(this.fitToFrame && !this.ignoreResize(width, height)){
53555             if(this.fitContainer && this.resizeEl != this.el){
53556                 this.el.setSize(width, height);
53557             }
53558             var size = this.adjustForComponents(width, height);
53559             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53560             this.fireEvent('resize', this, size.width, size.height);
53561         }
53562     },
53563     
53564     /**
53565      * Returns this panel's title
53566      * @return {String} 
53567      */
53568     getTitle : function(){
53569         return this.title;
53570     },
53571     
53572     /**
53573      * Set this panel's title
53574      * @param {String} title
53575      */
53576     setTitle : function(title){
53577         this.title = title;
53578         if(this.region){
53579             this.region.updatePanelTitle(this, title);
53580         }
53581     },
53582     
53583     /**
53584      * Returns true is this panel was configured to be closable
53585      * @return {Boolean} 
53586      */
53587     isClosable : function(){
53588         return this.closable;
53589     },
53590     
53591     beforeSlide : function(){
53592         this.el.clip();
53593         this.resizeEl.clip();
53594     },
53595     
53596     afterSlide : function(){
53597         this.el.unclip();
53598         this.resizeEl.unclip();
53599     },
53600     
53601     /**
53602      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53603      *   Will fail silently if the {@link #setUrl} method has not been called.
53604      *   This does not activate the panel, just updates its content.
53605      */
53606     refresh : function(){
53607         if(this.refreshDelegate){
53608            this.loaded = false;
53609            this.refreshDelegate();
53610         }
53611     },
53612     
53613     /**
53614      * Destroys this panel
53615      */
53616     destroy : function(){
53617         this.el.removeAllListeners();
53618         var tempEl = document.createElement("span");
53619         tempEl.appendChild(this.el.dom);
53620         tempEl.innerHTML = "";
53621         this.el.remove();
53622         this.el = null;
53623     },
53624     
53625     /**
53626      * form - if the content panel contains a form - this is a reference to it.
53627      * @type {Roo.form.Form}
53628      */
53629     form : false,
53630     /**
53631      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53632      *    This contains a reference to it.
53633      * @type {Roo.View}
53634      */
53635     view : false,
53636     
53637       /**
53638      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53639      * <pre><code>
53640
53641 layout.addxtype({
53642        xtype : 'Form',
53643        items: [ .... ]
53644    }
53645 );
53646
53647 </code></pre>
53648      * @param {Object} cfg Xtype definition of item to add.
53649      */
53650     
53651     addxtype : function(cfg) {
53652         // add form..
53653         if (cfg.xtype.match(/^Form$/)) {
53654             
53655             var el;
53656             //if (this.footer) {
53657             //    el = this.footer.container.insertSibling(false, 'before');
53658             //} else {
53659                 el = this.el.createChild();
53660             //}
53661
53662             this.form = new  Roo.form.Form(cfg);
53663             
53664             
53665             if ( this.form.allItems.length) {
53666                 this.form.render(el.dom);
53667             }
53668             return this.form;
53669         }
53670         // should only have one of theses..
53671         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
53672             // views.. should not be just added - used named prop 'view''
53673             
53674             cfg.el = this.el.appendChild(document.createElement("div"));
53675             // factory?
53676             
53677             var ret = new Roo.factory(cfg);
53678              
53679              ret.render && ret.render(false, ''); // render blank..
53680             this.view = ret;
53681             return ret;
53682         }
53683         return false;
53684     }
53685 });
53686
53687 /**
53688  * @class Roo.GridPanel
53689  * @extends Roo.ContentPanel
53690  * @constructor
53691  * Create a new GridPanel.
53692  * @param {Roo.grid.Grid} grid The grid for this panel
53693  * @param {String/Object} config A string to set only the panel's title, or a config object
53694  */
53695 Roo.GridPanel = function(grid, config){
53696     
53697   
53698     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
53699         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
53700         
53701     this.wrapper.dom.appendChild(grid.getGridEl().dom);
53702     
53703     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
53704     
53705     if(this.toolbar){
53706         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
53707     }
53708     // xtype created footer. - not sure if will work as we normally have to render first..
53709     if (this.footer && !this.footer.el && this.footer.xtype) {
53710         
53711         this.footer.container = this.grid.getView().getFooterPanel(true);
53712         this.footer.dataSource = this.grid.dataSource;
53713         this.footer = Roo.factory(this.footer, Roo);
53714         
53715     }
53716     
53717     grid.monitorWindowResize = false; // turn off autosizing
53718     grid.autoHeight = false;
53719     grid.autoWidth = false;
53720     this.grid = grid;
53721     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
53722 };
53723
53724 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
53725     getId : function(){
53726         return this.grid.id;
53727     },
53728     
53729     /**
53730      * Returns the grid for this panel
53731      * @return {Roo.grid.Grid} 
53732      */
53733     getGrid : function(){
53734         return this.grid;    
53735     },
53736     
53737     setSize : function(width, height){
53738         if(!this.ignoreResize(width, height)){
53739             var grid = this.grid;
53740             var size = this.adjustForComponents(width, height);
53741             grid.getGridEl().setSize(size.width, size.height);
53742             grid.autoSize();
53743         }
53744     },
53745     
53746     beforeSlide : function(){
53747         this.grid.getView().scroller.clip();
53748     },
53749     
53750     afterSlide : function(){
53751         this.grid.getView().scroller.unclip();
53752     },
53753     
53754     destroy : function(){
53755         this.grid.destroy();
53756         delete this.grid;
53757         Roo.GridPanel.superclass.destroy.call(this); 
53758     }
53759 });
53760
53761
53762 /**
53763  * @class Roo.NestedLayoutPanel
53764  * @extends Roo.ContentPanel
53765  * @constructor
53766  * Create a new NestedLayoutPanel.
53767  * 
53768  * 
53769  * @param {Roo.BorderLayout} layout The layout for this panel
53770  * @param {String/Object} config A string to set only the title or a config object
53771  */
53772 Roo.NestedLayoutPanel = function(layout, config)
53773 {
53774     // construct with only one argument..
53775     /* FIXME - implement nicer consturctors
53776     if (layout.layout) {
53777         config = layout;
53778         layout = config.layout;
53779         delete config.layout;
53780     }
53781     if (layout.xtype && !layout.getEl) {
53782         // then layout needs constructing..
53783         layout = Roo.factory(layout, Roo);
53784     }
53785     */
53786     
53787     
53788     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
53789     
53790     layout.monitorWindowResize = false; // turn off autosizing
53791     this.layout = layout;
53792     this.layout.getEl().addClass("x-layout-nested-layout");
53793     
53794     
53795     
53796     
53797 };
53798
53799 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
53800
53801     setSize : function(width, height){
53802         if(!this.ignoreResize(width, height)){
53803             var size = this.adjustForComponents(width, height);
53804             var el = this.layout.getEl();
53805             el.setSize(size.width, size.height);
53806             var touch = el.dom.offsetWidth;
53807             this.layout.layout();
53808             // ie requires a double layout on the first pass
53809             if(Roo.isIE && !this.initialized){
53810                 this.initialized = true;
53811                 this.layout.layout();
53812             }
53813         }
53814     },
53815     
53816     // activate all subpanels if not currently active..
53817     
53818     setActiveState : function(active){
53819         this.active = active;
53820         if(!active){
53821             this.fireEvent("deactivate", this);
53822             return;
53823         }
53824         
53825         this.fireEvent("activate", this);
53826         // not sure if this should happen before or after..
53827         if (!this.layout) {
53828             return; // should not happen..
53829         }
53830         var reg = false;
53831         for (var r in this.layout.regions) {
53832             reg = this.layout.getRegion(r);
53833             if (reg.getActivePanel()) {
53834                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
53835                 reg.setActivePanel(reg.getActivePanel());
53836                 continue;
53837             }
53838             if (!reg.panels.length) {
53839                 continue;
53840             }
53841             reg.showPanel(reg.getPanel(0));
53842         }
53843         
53844         
53845         
53846         
53847     },
53848     
53849     /**
53850      * Returns the nested BorderLayout for this panel
53851      * @return {Roo.BorderLayout} 
53852      */
53853     getLayout : function(){
53854         return this.layout;
53855     },
53856     
53857      /**
53858      * Adds a xtype elements to the layout of the nested panel
53859      * <pre><code>
53860
53861 panel.addxtype({
53862        xtype : 'ContentPanel',
53863        region: 'west',
53864        items: [ .... ]
53865    }
53866 );
53867
53868 panel.addxtype({
53869         xtype : 'NestedLayoutPanel',
53870         region: 'west',
53871         layout: {
53872            center: { },
53873            west: { }   
53874         },
53875         items : [ ... list of content panels or nested layout panels.. ]
53876    }
53877 );
53878 </code></pre>
53879      * @param {Object} cfg Xtype definition of item to add.
53880      */
53881     addxtype : function(cfg) {
53882         return this.layout.addxtype(cfg);
53883     
53884     }
53885 });
53886
53887 Roo.ScrollPanel = function(el, config, content){
53888     config = config || {};
53889     config.fitToFrame = true;
53890     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
53891     
53892     this.el.dom.style.overflow = "hidden";
53893     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
53894     this.el.removeClass("x-layout-inactive-content");
53895     this.el.on("mousewheel", this.onWheel, this);
53896
53897     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
53898     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
53899     up.unselectable(); down.unselectable();
53900     up.on("click", this.scrollUp, this);
53901     down.on("click", this.scrollDown, this);
53902     up.addClassOnOver("x-scroller-btn-over");
53903     down.addClassOnOver("x-scroller-btn-over");
53904     up.addClassOnClick("x-scroller-btn-click");
53905     down.addClassOnClick("x-scroller-btn-click");
53906     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
53907
53908     this.resizeEl = this.el;
53909     this.el = wrap; this.up = up; this.down = down;
53910 };
53911
53912 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
53913     increment : 100,
53914     wheelIncrement : 5,
53915     scrollUp : function(){
53916         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
53917     },
53918
53919     scrollDown : function(){
53920         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
53921     },
53922
53923     afterScroll : function(){
53924         var el = this.resizeEl;
53925         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
53926         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53927         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53928     },
53929
53930     setSize : function(){
53931         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
53932         this.afterScroll();
53933     },
53934
53935     onWheel : function(e){
53936         var d = e.getWheelDelta();
53937         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
53938         this.afterScroll();
53939         e.stopEvent();
53940     },
53941
53942     setContent : function(content, loadScripts){
53943         this.resizeEl.update(content, loadScripts);
53944     }
53945
53946 });
53947
53948
53949
53950
53951
53952
53953
53954
53955
53956 /**
53957  * @class Roo.TreePanel
53958  * @extends Roo.ContentPanel
53959  * @constructor
53960  * Create a new TreePanel. - defaults to fit/scoll contents.
53961  * @param {String/Object} config A string to set only the panel's title, or a config object
53962  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
53963  */
53964 Roo.TreePanel = function(config){
53965     var el = config.el;
53966     var tree = config.tree;
53967     delete config.tree; 
53968     delete config.el; // hopefull!
53969     
53970     // wrapper for IE7 strict & safari scroll issue
53971     
53972     var treeEl = el.createChild();
53973     config.resizeEl = treeEl;
53974     
53975     
53976     
53977     Roo.TreePanel.superclass.constructor.call(this, el, config);
53978  
53979  
53980     this.tree = new Roo.tree.TreePanel(treeEl , tree);
53981     //console.log(tree);
53982     this.on('activate', function()
53983     {
53984         if (this.tree.rendered) {
53985             return;
53986         }
53987         //console.log('render tree');
53988         this.tree.render();
53989     });
53990     // this should not be needed.. - it's actually the 'el' that resizes?
53991     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
53992     
53993     //this.on('resize',  function (cp, w, h) {
53994     //        this.tree.innerCt.setWidth(w);
53995     //        this.tree.innerCt.setHeight(h);
53996     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
53997     //});
53998
53999         
54000     
54001 };
54002
54003 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
54004     fitToFrame : true,
54005     autoScroll : true
54006 });
54007
54008
54009
54010
54011
54012
54013
54014
54015
54016
54017
54018 /*
54019  * Based on:
54020  * Ext JS Library 1.1.1
54021  * Copyright(c) 2006-2007, Ext JS, LLC.
54022  *
54023  * Originally Released Under LGPL - original licence link has changed is not relivant.
54024  *
54025  * Fork - LGPL
54026  * <script type="text/javascript">
54027  */
54028  
54029
54030 /**
54031  * @class Roo.ReaderLayout
54032  * @extends Roo.BorderLayout
54033  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
54034  * center region containing two nested regions (a top one for a list view and one for item preview below),
54035  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
54036  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
54037  * expedites the setup of the overall layout and regions for this common application style.
54038  * Example:
54039  <pre><code>
54040 var reader = new Roo.ReaderLayout();
54041 var CP = Roo.ContentPanel;  // shortcut for adding
54042
54043 reader.beginUpdate();
54044 reader.add("north", new CP("north", "North"));
54045 reader.add("west", new CP("west", {title: "West"}));
54046 reader.add("east", new CP("east", {title: "East"}));
54047
54048 reader.regions.listView.add(new CP("listView", "List"));
54049 reader.regions.preview.add(new CP("preview", "Preview"));
54050 reader.endUpdate();
54051 </code></pre>
54052 * @constructor
54053 * Create a new ReaderLayout
54054 * @param {Object} config Configuration options
54055 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
54056 * document.body if omitted)
54057 */
54058 Roo.ReaderLayout = function(config, renderTo){
54059     var c = config || {size:{}};
54060     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
54061         north: c.north !== false ? Roo.apply({
54062             split:false,
54063             initialSize: 32,
54064             titlebar: false
54065         }, c.north) : false,
54066         west: c.west !== false ? Roo.apply({
54067             split:true,
54068             initialSize: 200,
54069             minSize: 175,
54070             maxSize: 400,
54071             titlebar: true,
54072             collapsible: true,
54073             animate: true,
54074             margins:{left:5,right:0,bottom:5,top:5},
54075             cmargins:{left:5,right:5,bottom:5,top:5}
54076         }, c.west) : false,
54077         east: c.east !== false ? Roo.apply({
54078             split:true,
54079             initialSize: 200,
54080             minSize: 175,
54081             maxSize: 400,
54082             titlebar: true,
54083             collapsible: true,
54084             animate: true,
54085             margins:{left:0,right:5,bottom:5,top:5},
54086             cmargins:{left:5,right:5,bottom:5,top:5}
54087         }, c.east) : false,
54088         center: Roo.apply({
54089             tabPosition: 'top',
54090             autoScroll:false,
54091             closeOnTab: true,
54092             titlebar:false,
54093             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
54094         }, c.center)
54095     });
54096
54097     this.el.addClass('x-reader');
54098
54099     this.beginUpdate();
54100
54101     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
54102         south: c.preview !== false ? Roo.apply({
54103             split:true,
54104             initialSize: 200,
54105             minSize: 100,
54106             autoScroll:true,
54107             collapsible:true,
54108             titlebar: true,
54109             cmargins:{top:5,left:0, right:0, bottom:0}
54110         }, c.preview) : false,
54111         center: Roo.apply({
54112             autoScroll:false,
54113             titlebar:false,
54114             minHeight:200
54115         }, c.listView)
54116     });
54117     this.add('center', new Roo.NestedLayoutPanel(inner,
54118             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
54119
54120     this.endUpdate();
54121
54122     this.regions.preview = inner.getRegion('south');
54123     this.regions.listView = inner.getRegion('center');
54124 };
54125
54126 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
54127  * Based on:
54128  * Ext JS Library 1.1.1
54129  * Copyright(c) 2006-2007, Ext JS, LLC.
54130  *
54131  * Originally Released Under LGPL - original licence link has changed is not relivant.
54132  *
54133  * Fork - LGPL
54134  * <script type="text/javascript">
54135  */
54136  
54137 /**
54138  * @class Roo.grid.Grid
54139  * @extends Roo.util.Observable
54140  * This class represents the primary interface of a component based grid control.
54141  * <br><br>Usage:<pre><code>
54142  var grid = new Roo.grid.Grid("my-container-id", {
54143      ds: myDataStore,
54144      cm: myColModel,
54145      selModel: mySelectionModel,
54146      autoSizeColumns: true,
54147      monitorWindowResize: false,
54148      trackMouseOver: true
54149  });
54150  // set any options
54151  grid.render();
54152  * </code></pre>
54153  * <b>Common Problems:</b><br/>
54154  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
54155  * element will correct this<br/>
54156  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
54157  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
54158  * are unpredictable.<br/>
54159  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
54160  * grid to calculate dimensions/offsets.<br/>
54161   * @constructor
54162  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54163  * The container MUST have some type of size defined for the grid to fill. The container will be
54164  * automatically set to position relative if it isn't already.
54165  * @param {Object} config A config object that sets properties on this grid.
54166  */
54167 Roo.grid.Grid = function(container, config){
54168         // initialize the container
54169         this.container = Roo.get(container);
54170         this.container.update("");
54171         this.container.setStyle("overflow", "hidden");
54172     this.container.addClass('x-grid-container');
54173
54174     this.id = this.container.id;
54175
54176     Roo.apply(this, config);
54177     // check and correct shorthanded configs
54178     if(this.ds){
54179         this.dataSource = this.ds;
54180         delete this.ds;
54181     }
54182     if(this.cm){
54183         this.colModel = this.cm;
54184         delete this.cm;
54185     }
54186     if(this.sm){
54187         this.selModel = this.sm;
54188         delete this.sm;
54189     }
54190
54191     if (this.selModel) {
54192         this.selModel = Roo.factory(this.selModel, Roo.grid);
54193         this.sm = this.selModel;
54194         this.sm.xmodule = this.xmodule || false;
54195     }
54196     if (typeof(this.colModel.config) == 'undefined') {
54197         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54198         this.cm = this.colModel;
54199         this.cm.xmodule = this.xmodule || false;
54200     }
54201     if (this.dataSource) {
54202         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54203         this.ds = this.dataSource;
54204         this.ds.xmodule = this.xmodule || false;
54205          
54206     }
54207     
54208     
54209     
54210     if(this.width){
54211         this.container.setWidth(this.width);
54212     }
54213
54214     if(this.height){
54215         this.container.setHeight(this.height);
54216     }
54217     /** @private */
54218         this.addEvents({
54219         // raw events
54220         /**
54221          * @event click
54222          * The raw click event for the entire grid.
54223          * @param {Roo.EventObject} e
54224          */
54225         "click" : true,
54226         /**
54227          * @event dblclick
54228          * The raw dblclick event for the entire grid.
54229          * @param {Roo.EventObject} e
54230          */
54231         "dblclick" : true,
54232         /**
54233          * @event contextmenu
54234          * The raw contextmenu event for the entire grid.
54235          * @param {Roo.EventObject} e
54236          */
54237         "contextmenu" : true,
54238         /**
54239          * @event mousedown
54240          * The raw mousedown event for the entire grid.
54241          * @param {Roo.EventObject} e
54242          */
54243         "mousedown" : true,
54244         /**
54245          * @event mouseup
54246          * The raw mouseup event for the entire grid.
54247          * @param {Roo.EventObject} e
54248          */
54249         "mouseup" : true,
54250         /**
54251          * @event mouseover
54252          * The raw mouseover event for the entire grid.
54253          * @param {Roo.EventObject} e
54254          */
54255         "mouseover" : true,
54256         /**
54257          * @event mouseout
54258          * The raw mouseout event for the entire grid.
54259          * @param {Roo.EventObject} e
54260          */
54261         "mouseout" : true,
54262         /**
54263          * @event keypress
54264          * The raw keypress event for the entire grid.
54265          * @param {Roo.EventObject} e
54266          */
54267         "keypress" : true,
54268         /**
54269          * @event keydown
54270          * The raw keydown event for the entire grid.
54271          * @param {Roo.EventObject} e
54272          */
54273         "keydown" : true,
54274
54275         // custom events
54276
54277         /**
54278          * @event cellclick
54279          * Fires when a cell is clicked
54280          * @param {Grid} this
54281          * @param {Number} rowIndex
54282          * @param {Number} columnIndex
54283          * @param {Roo.EventObject} e
54284          */
54285         "cellclick" : true,
54286         /**
54287          * @event celldblclick
54288          * Fires when a cell is double clicked
54289          * @param {Grid} this
54290          * @param {Number} rowIndex
54291          * @param {Number} columnIndex
54292          * @param {Roo.EventObject} e
54293          */
54294         "celldblclick" : true,
54295         /**
54296          * @event rowclick
54297          * Fires when a row is clicked
54298          * @param {Grid} this
54299          * @param {Number} rowIndex
54300          * @param {Roo.EventObject} e
54301          */
54302         "rowclick" : true,
54303         /**
54304          * @event rowdblclick
54305          * Fires when a row is double clicked
54306          * @param {Grid} this
54307          * @param {Number} rowIndex
54308          * @param {Roo.EventObject} e
54309          */
54310         "rowdblclick" : true,
54311         /**
54312          * @event headerclick
54313          * Fires when a header is clicked
54314          * @param {Grid} this
54315          * @param {Number} columnIndex
54316          * @param {Roo.EventObject} e
54317          */
54318         "headerclick" : true,
54319         /**
54320          * @event headerdblclick
54321          * Fires when a header cell is double clicked
54322          * @param {Grid} this
54323          * @param {Number} columnIndex
54324          * @param {Roo.EventObject} e
54325          */
54326         "headerdblclick" : true,
54327         /**
54328          * @event rowcontextmenu
54329          * Fires when a row is right clicked
54330          * @param {Grid} this
54331          * @param {Number} rowIndex
54332          * @param {Roo.EventObject} e
54333          */
54334         "rowcontextmenu" : true,
54335         /**
54336          * @event cellcontextmenu
54337          * Fires when a cell is right clicked
54338          * @param {Grid} this
54339          * @param {Number} rowIndex
54340          * @param {Number} cellIndex
54341          * @param {Roo.EventObject} e
54342          */
54343          "cellcontextmenu" : true,
54344         /**
54345          * @event headercontextmenu
54346          * Fires when a header is right clicked
54347          * @param {Grid} this
54348          * @param {Number} columnIndex
54349          * @param {Roo.EventObject} e
54350          */
54351         "headercontextmenu" : true,
54352         /**
54353          * @event bodyscroll
54354          * Fires when the body element is scrolled
54355          * @param {Number} scrollLeft
54356          * @param {Number} scrollTop
54357          */
54358         "bodyscroll" : true,
54359         /**
54360          * @event columnresize
54361          * Fires when the user resizes a column
54362          * @param {Number} columnIndex
54363          * @param {Number} newSize
54364          */
54365         "columnresize" : true,
54366         /**
54367          * @event columnmove
54368          * Fires when the user moves a column
54369          * @param {Number} oldIndex
54370          * @param {Number} newIndex
54371          */
54372         "columnmove" : true,
54373         /**
54374          * @event startdrag
54375          * Fires when row(s) start being dragged
54376          * @param {Grid} this
54377          * @param {Roo.GridDD} dd The drag drop object
54378          * @param {event} e The raw browser event
54379          */
54380         "startdrag" : true,
54381         /**
54382          * @event enddrag
54383          * Fires when a drag operation is complete
54384          * @param {Grid} this
54385          * @param {Roo.GridDD} dd The drag drop object
54386          * @param {event} e The raw browser event
54387          */
54388         "enddrag" : true,
54389         /**
54390          * @event dragdrop
54391          * Fires when dragged row(s) are dropped on a valid DD target
54392          * @param {Grid} this
54393          * @param {Roo.GridDD} dd The drag drop object
54394          * @param {String} targetId The target drag drop object
54395          * @param {event} e The raw browser event
54396          */
54397         "dragdrop" : true,
54398         /**
54399          * @event dragover
54400          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54401          * @param {Grid} this
54402          * @param {Roo.GridDD} dd The drag drop object
54403          * @param {String} targetId The target drag drop object
54404          * @param {event} e The raw browser event
54405          */
54406         "dragover" : true,
54407         /**
54408          * @event dragenter
54409          *  Fires when the dragged row(s) first cross another DD target while being dragged
54410          * @param {Grid} this
54411          * @param {Roo.GridDD} dd The drag drop object
54412          * @param {String} targetId The target drag drop object
54413          * @param {event} e The raw browser event
54414          */
54415         "dragenter" : true,
54416         /**
54417          * @event dragout
54418          * Fires when the dragged row(s) leave another DD target while being dragged
54419          * @param {Grid} this
54420          * @param {Roo.GridDD} dd The drag drop object
54421          * @param {String} targetId The target drag drop object
54422          * @param {event} e The raw browser event
54423          */
54424         "dragout" : true,
54425         /**
54426          * @event rowclass
54427          * Fires when a row is rendered, so you can change add a style to it.
54428          * @param {GridView} gridview   The grid view
54429          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54430          */
54431         'rowclass' : true,
54432
54433         /**
54434          * @event render
54435          * Fires when the grid is rendered
54436          * @param {Grid} grid
54437          */
54438         'render' : true
54439     });
54440
54441     Roo.grid.Grid.superclass.constructor.call(this);
54442 };
54443 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54444     
54445     /**
54446      * @cfg {String} ddGroup - drag drop group.
54447      */
54448
54449     /**
54450      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54451      */
54452     minColumnWidth : 25,
54453
54454     /**
54455      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54456      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54457      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54458      */
54459     autoSizeColumns : false,
54460
54461     /**
54462      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54463      */
54464     autoSizeHeaders : true,
54465
54466     /**
54467      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54468      */
54469     monitorWindowResize : true,
54470
54471     /**
54472      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54473      * rows measured to get a columns size. Default is 0 (all rows).
54474      */
54475     maxRowsToMeasure : 0,
54476
54477     /**
54478      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54479      */
54480     trackMouseOver : true,
54481
54482     /**
54483     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54484     */
54485     
54486     /**
54487     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54488     */
54489     enableDragDrop : false,
54490     
54491     /**
54492     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54493     */
54494     enableColumnMove : true,
54495     
54496     /**
54497     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54498     */
54499     enableColumnHide : true,
54500     
54501     /**
54502     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54503     */
54504     enableRowHeightSync : false,
54505     
54506     /**
54507     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54508     */
54509     stripeRows : true,
54510     
54511     /**
54512     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54513     */
54514     autoHeight : false,
54515
54516     /**
54517      * @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.
54518      */
54519     autoExpandColumn : false,
54520
54521     /**
54522     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54523     * Default is 50.
54524     */
54525     autoExpandMin : 50,
54526
54527     /**
54528     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54529     */
54530     autoExpandMax : 1000,
54531
54532     /**
54533     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54534     */
54535     view : null,
54536
54537     /**
54538     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54539     */
54540     loadMask : false,
54541     /**
54542     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54543     */
54544     dropTarget: false,
54545     
54546    
54547     
54548     // private
54549     rendered : false,
54550
54551     /**
54552     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54553     * of a fixed width. Default is false.
54554     */
54555     /**
54556     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54557     */
54558     /**
54559      * Called once after all setup has been completed and the grid is ready to be rendered.
54560      * @return {Roo.grid.Grid} this
54561      */
54562     render : function()
54563     {
54564         var c = this.container;
54565         // try to detect autoHeight/width mode
54566         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54567             this.autoHeight = true;
54568         }
54569         var view = this.getView();
54570         view.init(this);
54571
54572         c.on("click", this.onClick, this);
54573         c.on("dblclick", this.onDblClick, this);
54574         c.on("contextmenu", this.onContextMenu, this);
54575         c.on("keydown", this.onKeyDown, this);
54576         if (Roo.isTouch) {
54577             c.on("touchstart", this.onTouchStart, this);
54578         }
54579
54580         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54581
54582         this.getSelectionModel().init(this);
54583
54584         view.render();
54585
54586         if(this.loadMask){
54587             this.loadMask = new Roo.LoadMask(this.container,
54588                     Roo.apply({store:this.dataSource}, this.loadMask));
54589         }
54590         
54591         
54592         if (this.toolbar && this.toolbar.xtype) {
54593             this.toolbar.container = this.getView().getHeaderPanel(true);
54594             this.toolbar = new Roo.Toolbar(this.toolbar);
54595         }
54596         if (this.footer && this.footer.xtype) {
54597             this.footer.dataSource = this.getDataSource();
54598             this.footer.container = this.getView().getFooterPanel(true);
54599             this.footer = Roo.factory(this.footer, Roo);
54600         }
54601         if (this.dropTarget && this.dropTarget.xtype) {
54602             delete this.dropTarget.xtype;
54603             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54604         }
54605         
54606         
54607         this.rendered = true;
54608         this.fireEvent('render', this);
54609         return this;
54610     },
54611
54612         /**
54613          * Reconfigures the grid to use a different Store and Column Model.
54614          * The View will be bound to the new objects and refreshed.
54615          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54616          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54617          */
54618     reconfigure : function(dataSource, colModel){
54619         if(this.loadMask){
54620             this.loadMask.destroy();
54621             this.loadMask = new Roo.LoadMask(this.container,
54622                     Roo.apply({store:dataSource}, this.loadMask));
54623         }
54624         this.view.bind(dataSource, colModel);
54625         this.dataSource = dataSource;
54626         this.colModel = colModel;
54627         this.view.refresh(true);
54628     },
54629
54630     // private
54631     onKeyDown : function(e){
54632         this.fireEvent("keydown", e);
54633     },
54634
54635     /**
54636      * Destroy this grid.
54637      * @param {Boolean} removeEl True to remove the element
54638      */
54639     destroy : function(removeEl, keepListeners){
54640         if(this.loadMask){
54641             this.loadMask.destroy();
54642         }
54643         var c = this.container;
54644         c.removeAllListeners();
54645         this.view.destroy();
54646         this.colModel.purgeListeners();
54647         if(!keepListeners){
54648             this.purgeListeners();
54649         }
54650         c.update("");
54651         if(removeEl === true){
54652             c.remove();
54653         }
54654     },
54655
54656     // private
54657     processEvent : function(name, e){
54658         // does this fire select???
54659         //Roo.log('grid:processEvent '  + name);
54660         
54661         if (name != 'touchstart' ) {
54662             this.fireEvent(name, e);    
54663         }
54664         
54665         var t = e.getTarget();
54666         var v = this.view;
54667         var header = v.findHeaderIndex(t);
54668         if(header !== false){
54669             var ename = name == 'touchstart' ? 'click' : name;
54670              
54671             this.fireEvent("header" + ename, this, header, e);
54672         }else{
54673             var row = v.findRowIndex(t);
54674             var cell = v.findCellIndex(t);
54675             if (name == 'touchstart') {
54676                 // first touch is always a click.
54677                 // hopefull this happens after selection is updated.?
54678                 name = false;
54679                 
54680                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
54681                     var cs = this.selModel.getSelectedCell();
54682                     if (row == cs[0] && cell == cs[1]){
54683                         name = 'dblclick';
54684                     }
54685                 }
54686                 if (typeof(this.selModel.getSelections) != 'undefined') {
54687                     var cs = this.selModel.getSelections();
54688                     var ds = this.dataSource;
54689                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
54690                         name = 'dblclick';
54691                     }
54692                 }
54693                 if (!name) {
54694                     return;
54695                 }
54696             }
54697             
54698             
54699             if(row !== false){
54700                 this.fireEvent("row" + name, this, row, e);
54701                 if(cell !== false){
54702                     this.fireEvent("cell" + name, this, row, cell, e);
54703                 }
54704             }
54705         }
54706     },
54707
54708     // private
54709     onClick : function(e){
54710         this.processEvent("click", e);
54711     },
54712    // private
54713     onTouchStart : function(e){
54714         this.processEvent("touchstart", e);
54715     },
54716
54717     // private
54718     onContextMenu : function(e, t){
54719         this.processEvent("contextmenu", e);
54720     },
54721
54722     // private
54723     onDblClick : function(e){
54724         this.processEvent("dblclick", e);
54725     },
54726
54727     // private
54728     walkCells : function(row, col, step, fn, scope){
54729         var cm = this.colModel, clen = cm.getColumnCount();
54730         var ds = this.dataSource, rlen = ds.getCount(), first = true;
54731         if(step < 0){
54732             if(col < 0){
54733                 row--;
54734                 first = false;
54735             }
54736             while(row >= 0){
54737                 if(!first){
54738                     col = clen-1;
54739                 }
54740                 first = false;
54741                 while(col >= 0){
54742                     if(fn.call(scope || this, row, col, cm) === true){
54743                         return [row, col];
54744                     }
54745                     col--;
54746                 }
54747                 row--;
54748             }
54749         } else {
54750             if(col >= clen){
54751                 row++;
54752                 first = false;
54753             }
54754             while(row < rlen){
54755                 if(!first){
54756                     col = 0;
54757                 }
54758                 first = false;
54759                 while(col < clen){
54760                     if(fn.call(scope || this, row, col, cm) === true){
54761                         return [row, col];
54762                     }
54763                     col++;
54764                 }
54765                 row++;
54766             }
54767         }
54768         return null;
54769     },
54770
54771     // private
54772     getSelections : function(){
54773         return this.selModel.getSelections();
54774     },
54775
54776     /**
54777      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
54778      * but if manual update is required this method will initiate it.
54779      */
54780     autoSize : function(){
54781         if(this.rendered){
54782             this.view.layout();
54783             if(this.view.adjustForScroll){
54784                 this.view.adjustForScroll();
54785             }
54786         }
54787     },
54788
54789     /**
54790      * Returns the grid's underlying element.
54791      * @return {Element} The element
54792      */
54793     getGridEl : function(){
54794         return this.container;
54795     },
54796
54797     // private for compatibility, overridden by editor grid
54798     stopEditing : function(){},
54799
54800     /**
54801      * Returns the grid's SelectionModel.
54802      * @return {SelectionModel}
54803      */
54804     getSelectionModel : function(){
54805         if(!this.selModel){
54806             this.selModel = new Roo.grid.RowSelectionModel();
54807         }
54808         return this.selModel;
54809     },
54810
54811     /**
54812      * Returns the grid's DataSource.
54813      * @return {DataSource}
54814      */
54815     getDataSource : function(){
54816         return this.dataSource;
54817     },
54818
54819     /**
54820      * Returns the grid's ColumnModel.
54821      * @return {ColumnModel}
54822      */
54823     getColumnModel : function(){
54824         return this.colModel;
54825     },
54826
54827     /**
54828      * Returns the grid's GridView object.
54829      * @return {GridView}
54830      */
54831     getView : function(){
54832         if(!this.view){
54833             this.view = new Roo.grid.GridView(this.viewConfig);
54834         }
54835         return this.view;
54836     },
54837     /**
54838      * Called to get grid's drag proxy text, by default returns this.ddText.
54839      * @return {String}
54840      */
54841     getDragDropText : function(){
54842         var count = this.selModel.getCount();
54843         return String.format(this.ddText, count, count == 1 ? '' : 's');
54844     }
54845 });
54846 /**
54847  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
54848  * %0 is replaced with the number of selected rows.
54849  * @type String
54850  */
54851 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
54852  * Based on:
54853  * Ext JS Library 1.1.1
54854  * Copyright(c) 2006-2007, Ext JS, LLC.
54855  *
54856  * Originally Released Under LGPL - original licence link has changed is not relivant.
54857  *
54858  * Fork - LGPL
54859  * <script type="text/javascript">
54860  */
54861  
54862 Roo.grid.AbstractGridView = function(){
54863         this.grid = null;
54864         
54865         this.events = {
54866             "beforerowremoved" : true,
54867             "beforerowsinserted" : true,
54868             "beforerefresh" : true,
54869             "rowremoved" : true,
54870             "rowsinserted" : true,
54871             "rowupdated" : true,
54872             "refresh" : true
54873         };
54874     Roo.grid.AbstractGridView.superclass.constructor.call(this);
54875 };
54876
54877 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
54878     rowClass : "x-grid-row",
54879     cellClass : "x-grid-cell",
54880     tdClass : "x-grid-td",
54881     hdClass : "x-grid-hd",
54882     splitClass : "x-grid-hd-split",
54883     
54884     init: function(grid){
54885         this.grid = grid;
54886                 var cid = this.grid.getGridEl().id;
54887         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
54888         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
54889         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
54890         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
54891         },
54892         
54893     getColumnRenderers : function(){
54894         var renderers = [];
54895         var cm = this.grid.colModel;
54896         var colCount = cm.getColumnCount();
54897         for(var i = 0; i < colCount; i++){
54898             renderers[i] = cm.getRenderer(i);
54899         }
54900         return renderers;
54901     },
54902     
54903     getColumnIds : function(){
54904         var ids = [];
54905         var cm = this.grid.colModel;
54906         var colCount = cm.getColumnCount();
54907         for(var i = 0; i < colCount; i++){
54908             ids[i] = cm.getColumnId(i);
54909         }
54910         return ids;
54911     },
54912     
54913     getDataIndexes : function(){
54914         if(!this.indexMap){
54915             this.indexMap = this.buildIndexMap();
54916         }
54917         return this.indexMap.colToData;
54918     },
54919     
54920     getColumnIndexByDataIndex : function(dataIndex){
54921         if(!this.indexMap){
54922             this.indexMap = this.buildIndexMap();
54923         }
54924         return this.indexMap.dataToCol[dataIndex];
54925     },
54926     
54927     /**
54928      * Set a css style for a column dynamically. 
54929      * @param {Number} colIndex The index of the column
54930      * @param {String} name The css property name
54931      * @param {String} value The css value
54932      */
54933     setCSSStyle : function(colIndex, name, value){
54934         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
54935         Roo.util.CSS.updateRule(selector, name, value);
54936     },
54937     
54938     generateRules : function(cm){
54939         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
54940         Roo.util.CSS.removeStyleSheet(rulesId);
54941         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54942             var cid = cm.getColumnId(i);
54943             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
54944                          this.tdSelector, cid, " {\n}\n",
54945                          this.hdSelector, cid, " {\n}\n",
54946                          this.splitSelector, cid, " {\n}\n");
54947         }
54948         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54949     }
54950 });/*
54951  * Based on:
54952  * Ext JS Library 1.1.1
54953  * Copyright(c) 2006-2007, Ext JS, LLC.
54954  *
54955  * Originally Released Under LGPL - original licence link has changed is not relivant.
54956  *
54957  * Fork - LGPL
54958  * <script type="text/javascript">
54959  */
54960
54961 // private
54962 // This is a support class used internally by the Grid components
54963 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
54964     this.grid = grid;
54965     this.view = grid.getView();
54966     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54967     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
54968     if(hd2){
54969         this.setHandleElId(Roo.id(hd));
54970         this.setOuterHandleElId(Roo.id(hd2));
54971     }
54972     this.scroll = false;
54973 };
54974 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
54975     maxDragWidth: 120,
54976     getDragData : function(e){
54977         var t = Roo.lib.Event.getTarget(e);
54978         var h = this.view.findHeaderCell(t);
54979         if(h){
54980             return {ddel: h.firstChild, header:h};
54981         }
54982         return false;
54983     },
54984
54985     onInitDrag : function(e){
54986         this.view.headersDisabled = true;
54987         var clone = this.dragData.ddel.cloneNode(true);
54988         clone.id = Roo.id();
54989         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
54990         this.proxy.update(clone);
54991         return true;
54992     },
54993
54994     afterValidDrop : function(){
54995         var v = this.view;
54996         setTimeout(function(){
54997             v.headersDisabled = false;
54998         }, 50);
54999     },
55000
55001     afterInvalidDrop : function(){
55002         var v = this.view;
55003         setTimeout(function(){
55004             v.headersDisabled = false;
55005         }, 50);
55006     }
55007 });
55008 /*
55009  * Based on:
55010  * Ext JS Library 1.1.1
55011  * Copyright(c) 2006-2007, Ext JS, LLC.
55012  *
55013  * Originally Released Under LGPL - original licence link has changed is not relivant.
55014  *
55015  * Fork - LGPL
55016  * <script type="text/javascript">
55017  */
55018 // private
55019 // This is a support class used internally by the Grid components
55020 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
55021     this.grid = grid;
55022     this.view = grid.getView();
55023     // split the proxies so they don't interfere with mouse events
55024     this.proxyTop = Roo.DomHelper.append(document.body, {
55025         cls:"col-move-top", html:"&#160;"
55026     }, true);
55027     this.proxyBottom = Roo.DomHelper.append(document.body, {
55028         cls:"col-move-bottom", html:"&#160;"
55029     }, true);
55030     this.proxyTop.hide = this.proxyBottom.hide = function(){
55031         this.setLeftTop(-100,-100);
55032         this.setStyle("visibility", "hidden");
55033     };
55034     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55035     // temporarily disabled
55036     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
55037     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
55038 };
55039 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
55040     proxyOffsets : [-4, -9],
55041     fly: Roo.Element.fly,
55042
55043     getTargetFromEvent : function(e){
55044         var t = Roo.lib.Event.getTarget(e);
55045         var cindex = this.view.findCellIndex(t);
55046         if(cindex !== false){
55047             return this.view.getHeaderCell(cindex);
55048         }
55049         return null;
55050     },
55051
55052     nextVisible : function(h){
55053         var v = this.view, cm = this.grid.colModel;
55054         h = h.nextSibling;
55055         while(h){
55056             if(!cm.isHidden(v.getCellIndex(h))){
55057                 return h;
55058             }
55059             h = h.nextSibling;
55060         }
55061         return null;
55062     },
55063
55064     prevVisible : function(h){
55065         var v = this.view, cm = this.grid.colModel;
55066         h = h.prevSibling;
55067         while(h){
55068             if(!cm.isHidden(v.getCellIndex(h))){
55069                 return h;
55070             }
55071             h = h.prevSibling;
55072         }
55073         return null;
55074     },
55075
55076     positionIndicator : function(h, n, e){
55077         var x = Roo.lib.Event.getPageX(e);
55078         var r = Roo.lib.Dom.getRegion(n.firstChild);
55079         var px, pt, py = r.top + this.proxyOffsets[1];
55080         if((r.right - x) <= (r.right-r.left)/2){
55081             px = r.right+this.view.borderWidth;
55082             pt = "after";
55083         }else{
55084             px = r.left;
55085             pt = "before";
55086         }
55087         var oldIndex = this.view.getCellIndex(h);
55088         var newIndex = this.view.getCellIndex(n);
55089
55090         if(this.grid.colModel.isFixed(newIndex)){
55091             return false;
55092         }
55093
55094         var locked = this.grid.colModel.isLocked(newIndex);
55095
55096         if(pt == "after"){
55097             newIndex++;
55098         }
55099         if(oldIndex < newIndex){
55100             newIndex--;
55101         }
55102         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
55103             return false;
55104         }
55105         px +=  this.proxyOffsets[0];
55106         this.proxyTop.setLeftTop(px, py);
55107         this.proxyTop.show();
55108         if(!this.bottomOffset){
55109             this.bottomOffset = this.view.mainHd.getHeight();
55110         }
55111         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
55112         this.proxyBottom.show();
55113         return pt;
55114     },
55115
55116     onNodeEnter : function(n, dd, e, data){
55117         if(data.header != n){
55118             this.positionIndicator(data.header, n, e);
55119         }
55120     },
55121
55122     onNodeOver : function(n, dd, e, data){
55123         var result = false;
55124         if(data.header != n){
55125             result = this.positionIndicator(data.header, n, e);
55126         }
55127         if(!result){
55128             this.proxyTop.hide();
55129             this.proxyBottom.hide();
55130         }
55131         return result ? this.dropAllowed : this.dropNotAllowed;
55132     },
55133
55134     onNodeOut : function(n, dd, e, data){
55135         this.proxyTop.hide();
55136         this.proxyBottom.hide();
55137     },
55138
55139     onNodeDrop : function(n, dd, e, data){
55140         var h = data.header;
55141         if(h != n){
55142             var cm = this.grid.colModel;
55143             var x = Roo.lib.Event.getPageX(e);
55144             var r = Roo.lib.Dom.getRegion(n.firstChild);
55145             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
55146             var oldIndex = this.view.getCellIndex(h);
55147             var newIndex = this.view.getCellIndex(n);
55148             var locked = cm.isLocked(newIndex);
55149             if(pt == "after"){
55150                 newIndex++;
55151             }
55152             if(oldIndex < newIndex){
55153                 newIndex--;
55154             }
55155             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
55156                 return false;
55157             }
55158             cm.setLocked(oldIndex, locked, true);
55159             cm.moveColumn(oldIndex, newIndex);
55160             this.grid.fireEvent("columnmove", oldIndex, newIndex);
55161             return true;
55162         }
55163         return false;
55164     }
55165 });
55166 /*
55167  * Based on:
55168  * Ext JS Library 1.1.1
55169  * Copyright(c) 2006-2007, Ext JS, LLC.
55170  *
55171  * Originally Released Under LGPL - original licence link has changed is not relivant.
55172  *
55173  * Fork - LGPL
55174  * <script type="text/javascript">
55175  */
55176   
55177 /**
55178  * @class Roo.grid.GridView
55179  * @extends Roo.util.Observable
55180  *
55181  * @constructor
55182  * @param {Object} config
55183  */
55184 Roo.grid.GridView = function(config){
55185     Roo.grid.GridView.superclass.constructor.call(this);
55186     this.el = null;
55187
55188     Roo.apply(this, config);
55189 };
55190
55191 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55192
55193     unselectable :  'unselectable="on"',
55194     unselectableCls :  'x-unselectable',
55195     
55196     
55197     rowClass : "x-grid-row",
55198
55199     cellClass : "x-grid-col",
55200
55201     tdClass : "x-grid-td",
55202
55203     hdClass : "x-grid-hd",
55204
55205     splitClass : "x-grid-split",
55206
55207     sortClasses : ["sort-asc", "sort-desc"],
55208
55209     enableMoveAnim : false,
55210
55211     hlColor: "C3DAF9",
55212
55213     dh : Roo.DomHelper,
55214
55215     fly : Roo.Element.fly,
55216
55217     css : Roo.util.CSS,
55218
55219     borderWidth: 1,
55220
55221     splitOffset: 3,
55222
55223     scrollIncrement : 22,
55224
55225     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55226
55227     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55228
55229     bind : function(ds, cm){
55230         if(this.ds){
55231             this.ds.un("load", this.onLoad, this);
55232             this.ds.un("datachanged", this.onDataChange, this);
55233             this.ds.un("add", this.onAdd, this);
55234             this.ds.un("remove", this.onRemove, this);
55235             this.ds.un("update", this.onUpdate, this);
55236             this.ds.un("clear", this.onClear, this);
55237         }
55238         if(ds){
55239             ds.on("load", this.onLoad, this);
55240             ds.on("datachanged", this.onDataChange, this);
55241             ds.on("add", this.onAdd, this);
55242             ds.on("remove", this.onRemove, this);
55243             ds.on("update", this.onUpdate, this);
55244             ds.on("clear", this.onClear, this);
55245         }
55246         this.ds = ds;
55247
55248         if(this.cm){
55249             this.cm.un("widthchange", this.onColWidthChange, this);
55250             this.cm.un("headerchange", this.onHeaderChange, this);
55251             this.cm.un("hiddenchange", this.onHiddenChange, this);
55252             this.cm.un("columnmoved", this.onColumnMove, this);
55253             this.cm.un("columnlockchange", this.onColumnLock, this);
55254         }
55255         if(cm){
55256             this.generateRules(cm);
55257             cm.on("widthchange", this.onColWidthChange, this);
55258             cm.on("headerchange", this.onHeaderChange, this);
55259             cm.on("hiddenchange", this.onHiddenChange, this);
55260             cm.on("columnmoved", this.onColumnMove, this);
55261             cm.on("columnlockchange", this.onColumnLock, this);
55262         }
55263         this.cm = cm;
55264     },
55265
55266     init: function(grid){
55267         Roo.grid.GridView.superclass.init.call(this, grid);
55268
55269         this.bind(grid.dataSource, grid.colModel);
55270
55271         grid.on("headerclick", this.handleHeaderClick, this);
55272
55273         if(grid.trackMouseOver){
55274             grid.on("mouseover", this.onRowOver, this);
55275             grid.on("mouseout", this.onRowOut, this);
55276         }
55277         grid.cancelTextSelection = function(){};
55278         this.gridId = grid.id;
55279
55280         var tpls = this.templates || {};
55281
55282         if(!tpls.master){
55283             tpls.master = new Roo.Template(
55284                '<div class="x-grid" hidefocus="true">',
55285                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55286                   '<div class="x-grid-topbar"></div>',
55287                   '<div class="x-grid-scroller"><div></div></div>',
55288                   '<div class="x-grid-locked">',
55289                       '<div class="x-grid-header">{lockedHeader}</div>',
55290                       '<div class="x-grid-body">{lockedBody}</div>',
55291                   "</div>",
55292                   '<div class="x-grid-viewport">',
55293                       '<div class="x-grid-header">{header}</div>',
55294                       '<div class="x-grid-body">{body}</div>',
55295                   "</div>",
55296                   '<div class="x-grid-bottombar"></div>',
55297                  
55298                   '<div class="x-grid-resize-proxy">&#160;</div>',
55299                "</div>"
55300             );
55301             tpls.master.disableformats = true;
55302         }
55303
55304         if(!tpls.header){
55305             tpls.header = new Roo.Template(
55306                '<table border="0" cellspacing="0" cellpadding="0">',
55307                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55308                "</table>{splits}"
55309             );
55310             tpls.header.disableformats = true;
55311         }
55312         tpls.header.compile();
55313
55314         if(!tpls.hcell){
55315             tpls.hcell = new Roo.Template(
55316                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55317                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55318                 "</div></td>"
55319              );
55320              tpls.hcell.disableFormats = true;
55321         }
55322         tpls.hcell.compile();
55323
55324         if(!tpls.hsplit){
55325             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55326                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55327             tpls.hsplit.disableFormats = true;
55328         }
55329         tpls.hsplit.compile();
55330
55331         if(!tpls.body){
55332             tpls.body = new Roo.Template(
55333                '<table border="0" cellspacing="0" cellpadding="0">',
55334                "<tbody>{rows}</tbody>",
55335                "</table>"
55336             );
55337             tpls.body.disableFormats = true;
55338         }
55339         tpls.body.compile();
55340
55341         if(!tpls.row){
55342             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55343             tpls.row.disableFormats = true;
55344         }
55345         tpls.row.compile();
55346
55347         if(!tpls.cell){
55348             tpls.cell = new Roo.Template(
55349                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55350                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55351                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55352                 "</td>"
55353             );
55354             tpls.cell.disableFormats = true;
55355         }
55356         tpls.cell.compile();
55357
55358         this.templates = tpls;
55359     },
55360
55361     // remap these for backwards compat
55362     onColWidthChange : function(){
55363         this.updateColumns.apply(this, arguments);
55364     },
55365     onHeaderChange : function(){
55366         this.updateHeaders.apply(this, arguments);
55367     }, 
55368     onHiddenChange : function(){
55369         this.handleHiddenChange.apply(this, arguments);
55370     },
55371     onColumnMove : function(){
55372         this.handleColumnMove.apply(this, arguments);
55373     },
55374     onColumnLock : function(){
55375         this.handleLockChange.apply(this, arguments);
55376     },
55377
55378     onDataChange : function(){
55379         this.refresh();
55380         this.updateHeaderSortState();
55381     },
55382
55383     onClear : function(){
55384         this.refresh();
55385     },
55386
55387     onUpdate : function(ds, record){
55388         this.refreshRow(record);
55389     },
55390
55391     refreshRow : function(record){
55392         var ds = this.ds, index;
55393         if(typeof record == 'number'){
55394             index = record;
55395             record = ds.getAt(index);
55396         }else{
55397             index = ds.indexOf(record);
55398         }
55399         this.insertRows(ds, index, index, true);
55400         this.onRemove(ds, record, index+1, true);
55401         this.syncRowHeights(index, index);
55402         this.layout();
55403         this.fireEvent("rowupdated", this, index, record);
55404     },
55405
55406     onAdd : function(ds, records, index){
55407         this.insertRows(ds, index, index + (records.length-1));
55408     },
55409
55410     onRemove : function(ds, record, index, isUpdate){
55411         if(isUpdate !== true){
55412             this.fireEvent("beforerowremoved", this, index, record);
55413         }
55414         var bt = this.getBodyTable(), lt = this.getLockedTable();
55415         if(bt.rows[index]){
55416             bt.firstChild.removeChild(bt.rows[index]);
55417         }
55418         if(lt.rows[index]){
55419             lt.firstChild.removeChild(lt.rows[index]);
55420         }
55421         if(isUpdate !== true){
55422             this.stripeRows(index);
55423             this.syncRowHeights(index, index);
55424             this.layout();
55425             this.fireEvent("rowremoved", this, index, record);
55426         }
55427     },
55428
55429     onLoad : function(){
55430         this.scrollToTop();
55431     },
55432
55433     /**
55434      * Scrolls the grid to the top
55435      */
55436     scrollToTop : function(){
55437         if(this.scroller){
55438             this.scroller.dom.scrollTop = 0;
55439             this.syncScroll();
55440         }
55441     },
55442
55443     /**
55444      * Gets a panel in the header of the grid that can be used for toolbars etc.
55445      * After modifying the contents of this panel a call to grid.autoSize() may be
55446      * required to register any changes in size.
55447      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55448      * @return Roo.Element
55449      */
55450     getHeaderPanel : function(doShow){
55451         if(doShow){
55452             this.headerPanel.show();
55453         }
55454         return this.headerPanel;
55455     },
55456
55457     /**
55458      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55459      * After modifying the contents of this panel a call to grid.autoSize() may be
55460      * required to register any changes in size.
55461      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55462      * @return Roo.Element
55463      */
55464     getFooterPanel : function(doShow){
55465         if(doShow){
55466             this.footerPanel.show();
55467         }
55468         return this.footerPanel;
55469     },
55470
55471     initElements : function(){
55472         var E = Roo.Element;
55473         var el = this.grid.getGridEl().dom.firstChild;
55474         var cs = el.childNodes;
55475
55476         this.el = new E(el);
55477         
55478          this.focusEl = new E(el.firstChild);
55479         this.focusEl.swallowEvent("click", true);
55480         
55481         this.headerPanel = new E(cs[1]);
55482         this.headerPanel.enableDisplayMode("block");
55483
55484         this.scroller = new E(cs[2]);
55485         this.scrollSizer = new E(this.scroller.dom.firstChild);
55486
55487         this.lockedWrap = new E(cs[3]);
55488         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55489         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55490
55491         this.mainWrap = new E(cs[4]);
55492         this.mainHd = new E(this.mainWrap.dom.firstChild);
55493         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55494
55495         this.footerPanel = new E(cs[5]);
55496         this.footerPanel.enableDisplayMode("block");
55497
55498         this.resizeProxy = new E(cs[6]);
55499
55500         this.headerSelector = String.format(
55501            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55502            this.lockedHd.id, this.mainHd.id
55503         );
55504
55505         this.splitterSelector = String.format(
55506            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55507            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55508         );
55509     },
55510     idToCssName : function(s)
55511     {
55512         return s.replace(/[^a-z0-9]+/ig, '-');
55513     },
55514
55515     getHeaderCell : function(index){
55516         return Roo.DomQuery.select(this.headerSelector)[index];
55517     },
55518
55519     getHeaderCellMeasure : function(index){
55520         return this.getHeaderCell(index).firstChild;
55521     },
55522
55523     getHeaderCellText : function(index){
55524         return this.getHeaderCell(index).firstChild.firstChild;
55525     },
55526
55527     getLockedTable : function(){
55528         return this.lockedBody.dom.firstChild;
55529     },
55530
55531     getBodyTable : function(){
55532         return this.mainBody.dom.firstChild;
55533     },
55534
55535     getLockedRow : function(index){
55536         return this.getLockedTable().rows[index];
55537     },
55538
55539     getRow : function(index){
55540         return this.getBodyTable().rows[index];
55541     },
55542
55543     getRowComposite : function(index){
55544         if(!this.rowEl){
55545             this.rowEl = new Roo.CompositeElementLite();
55546         }
55547         var els = [], lrow, mrow;
55548         if(lrow = this.getLockedRow(index)){
55549             els.push(lrow);
55550         }
55551         if(mrow = this.getRow(index)){
55552             els.push(mrow);
55553         }
55554         this.rowEl.elements = els;
55555         return this.rowEl;
55556     },
55557     /**
55558      * Gets the 'td' of the cell
55559      * 
55560      * @param {Integer} rowIndex row to select
55561      * @param {Integer} colIndex column to select
55562      * 
55563      * @return {Object} 
55564      */
55565     getCell : function(rowIndex, colIndex){
55566         var locked = this.cm.getLockedCount();
55567         var source;
55568         if(colIndex < locked){
55569             source = this.lockedBody.dom.firstChild;
55570         }else{
55571             source = this.mainBody.dom.firstChild;
55572             colIndex -= locked;
55573         }
55574         return source.rows[rowIndex].childNodes[colIndex];
55575     },
55576
55577     getCellText : function(rowIndex, colIndex){
55578         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55579     },
55580
55581     getCellBox : function(cell){
55582         var b = this.fly(cell).getBox();
55583         if(Roo.isOpera){ // opera fails to report the Y
55584             b.y = cell.offsetTop + this.mainBody.getY();
55585         }
55586         return b;
55587     },
55588
55589     getCellIndex : function(cell){
55590         var id = String(cell.className).match(this.cellRE);
55591         if(id){
55592             return parseInt(id[1], 10);
55593         }
55594         return 0;
55595     },
55596
55597     findHeaderIndex : function(n){
55598         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55599         return r ? this.getCellIndex(r) : false;
55600     },
55601
55602     findHeaderCell : function(n){
55603         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55604         return r ? r : false;
55605     },
55606
55607     findRowIndex : function(n){
55608         if(!n){
55609             return false;
55610         }
55611         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55612         return r ? r.rowIndex : false;
55613     },
55614
55615     findCellIndex : function(node){
55616         var stop = this.el.dom;
55617         while(node && node != stop){
55618             if(this.findRE.test(node.className)){
55619                 return this.getCellIndex(node);
55620             }
55621             node = node.parentNode;
55622         }
55623         return false;
55624     },
55625
55626     getColumnId : function(index){
55627         return this.cm.getColumnId(index);
55628     },
55629
55630     getSplitters : function()
55631     {
55632         if(this.splitterSelector){
55633            return Roo.DomQuery.select(this.splitterSelector);
55634         }else{
55635             return null;
55636       }
55637     },
55638
55639     getSplitter : function(index){
55640         return this.getSplitters()[index];
55641     },
55642
55643     onRowOver : function(e, t){
55644         var row;
55645         if((row = this.findRowIndex(t)) !== false){
55646             this.getRowComposite(row).addClass("x-grid-row-over");
55647         }
55648     },
55649
55650     onRowOut : function(e, t){
55651         var row;
55652         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
55653             this.getRowComposite(row).removeClass("x-grid-row-over");
55654         }
55655     },
55656
55657     renderHeaders : function(){
55658         var cm = this.cm;
55659         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
55660         var cb = [], lb = [], sb = [], lsb = [], p = {};
55661         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55662             p.cellId = "x-grid-hd-0-" + i;
55663             p.splitId = "x-grid-csplit-0-" + i;
55664             p.id = cm.getColumnId(i);
55665             p.value = cm.getColumnHeader(i) || "";
55666             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
55667             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
55668             if(!cm.isLocked(i)){
55669                 cb[cb.length] = ct.apply(p);
55670                 sb[sb.length] = st.apply(p);
55671             }else{
55672                 lb[lb.length] = ct.apply(p);
55673                 lsb[lsb.length] = st.apply(p);
55674             }
55675         }
55676         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
55677                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
55678     },
55679
55680     updateHeaders : function(){
55681         var html = this.renderHeaders();
55682         this.lockedHd.update(html[0]);
55683         this.mainHd.update(html[1]);
55684     },
55685
55686     /**
55687      * Focuses the specified row.
55688      * @param {Number} row The row index
55689      */
55690     focusRow : function(row)
55691     {
55692         //Roo.log('GridView.focusRow');
55693         var x = this.scroller.dom.scrollLeft;
55694         this.focusCell(row, 0, false);
55695         this.scroller.dom.scrollLeft = x;
55696     },
55697
55698     /**
55699      * Focuses the specified cell.
55700      * @param {Number} row The row index
55701      * @param {Number} col The column index
55702      * @param {Boolean} hscroll false to disable horizontal scrolling
55703      */
55704     focusCell : function(row, col, hscroll)
55705     {
55706         //Roo.log('GridView.focusCell');
55707         var el = this.ensureVisible(row, col, hscroll);
55708         this.focusEl.alignTo(el, "tl-tl");
55709         if(Roo.isGecko){
55710             this.focusEl.focus();
55711         }else{
55712             this.focusEl.focus.defer(1, this.focusEl);
55713         }
55714     },
55715
55716     /**
55717      * Scrolls the specified cell into view
55718      * @param {Number} row The row index
55719      * @param {Number} col The column index
55720      * @param {Boolean} hscroll false to disable horizontal scrolling
55721      */
55722     ensureVisible : function(row, col, hscroll)
55723     {
55724         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
55725         //return null; //disable for testing.
55726         if(typeof row != "number"){
55727             row = row.rowIndex;
55728         }
55729         if(row < 0 && row >= this.ds.getCount()){
55730             return  null;
55731         }
55732         col = (col !== undefined ? col : 0);
55733         var cm = this.grid.colModel;
55734         while(cm.isHidden(col)){
55735             col++;
55736         }
55737
55738         var el = this.getCell(row, col);
55739         if(!el){
55740             return null;
55741         }
55742         var c = this.scroller.dom;
55743
55744         var ctop = parseInt(el.offsetTop, 10);
55745         var cleft = parseInt(el.offsetLeft, 10);
55746         var cbot = ctop + el.offsetHeight;
55747         var cright = cleft + el.offsetWidth;
55748         
55749         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
55750         var stop = parseInt(c.scrollTop, 10);
55751         var sleft = parseInt(c.scrollLeft, 10);
55752         var sbot = stop + ch;
55753         var sright = sleft + c.clientWidth;
55754         /*
55755         Roo.log('GridView.ensureVisible:' +
55756                 ' ctop:' + ctop +
55757                 ' c.clientHeight:' + c.clientHeight +
55758                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
55759                 ' stop:' + stop +
55760                 ' cbot:' + cbot +
55761                 ' sbot:' + sbot +
55762                 ' ch:' + ch  
55763                 );
55764         */
55765         if(ctop < stop){
55766              c.scrollTop = ctop;
55767             //Roo.log("set scrolltop to ctop DISABLE?");
55768         }else if(cbot > sbot){
55769             //Roo.log("set scrolltop to cbot-ch");
55770             c.scrollTop = cbot-ch;
55771         }
55772         
55773         if(hscroll !== false){
55774             if(cleft < sleft){
55775                 c.scrollLeft = cleft;
55776             }else if(cright > sright){
55777                 c.scrollLeft = cright-c.clientWidth;
55778             }
55779         }
55780          
55781         return el;
55782     },
55783
55784     updateColumns : function(){
55785         this.grid.stopEditing();
55786         var cm = this.grid.colModel, colIds = this.getColumnIds();
55787         //var totalWidth = cm.getTotalWidth();
55788         var pos = 0;
55789         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55790             //if(cm.isHidden(i)) continue;
55791             var w = cm.getColumnWidth(i);
55792             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55793             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55794         }
55795         this.updateSplitters();
55796     },
55797
55798     generateRules : function(cm){
55799         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
55800         Roo.util.CSS.removeStyleSheet(rulesId);
55801         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55802             var cid = cm.getColumnId(i);
55803             var align = '';
55804             if(cm.config[i].align){
55805                 align = 'text-align:'+cm.config[i].align+';';
55806             }
55807             var hidden = '';
55808             if(cm.isHidden(i)){
55809                 hidden = 'display:none;';
55810             }
55811             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
55812             ruleBuf.push(
55813                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
55814                     this.hdSelector, cid, " {\n", align, width, "}\n",
55815                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
55816                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
55817         }
55818         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55819     },
55820
55821     updateSplitters : function(){
55822         var cm = this.cm, s = this.getSplitters();
55823         if(s){ // splitters not created yet
55824             var pos = 0, locked = true;
55825             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55826                 if(cm.isHidden(i)) {
55827                     continue;
55828                 }
55829                 var w = cm.getColumnWidth(i); // make sure it's a number
55830                 if(!cm.isLocked(i) && locked){
55831                     pos = 0;
55832                     locked = false;
55833                 }
55834                 pos += w;
55835                 s[i].style.left = (pos-this.splitOffset) + "px";
55836             }
55837         }
55838     },
55839
55840     handleHiddenChange : function(colModel, colIndex, hidden){
55841         if(hidden){
55842             this.hideColumn(colIndex);
55843         }else{
55844             this.unhideColumn(colIndex);
55845         }
55846     },
55847
55848     hideColumn : function(colIndex){
55849         var cid = this.getColumnId(colIndex);
55850         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
55851         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
55852         if(Roo.isSafari){
55853             this.updateHeaders();
55854         }
55855         this.updateSplitters();
55856         this.layout();
55857     },
55858
55859     unhideColumn : function(colIndex){
55860         var cid = this.getColumnId(colIndex);
55861         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
55862         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
55863
55864         if(Roo.isSafari){
55865             this.updateHeaders();
55866         }
55867         this.updateSplitters();
55868         this.layout();
55869     },
55870
55871     insertRows : function(dm, firstRow, lastRow, isUpdate){
55872         if(firstRow == 0 && lastRow == dm.getCount()-1){
55873             this.refresh();
55874         }else{
55875             if(!isUpdate){
55876                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
55877             }
55878             var s = this.getScrollState();
55879             var markup = this.renderRows(firstRow, lastRow);
55880             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
55881             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
55882             this.restoreScroll(s);
55883             if(!isUpdate){
55884                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
55885                 this.syncRowHeights(firstRow, lastRow);
55886                 this.stripeRows(firstRow);
55887                 this.layout();
55888             }
55889         }
55890     },
55891
55892     bufferRows : function(markup, target, index){
55893         var before = null, trows = target.rows, tbody = target.tBodies[0];
55894         if(index < trows.length){
55895             before = trows[index];
55896         }
55897         var b = document.createElement("div");
55898         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
55899         var rows = b.firstChild.rows;
55900         for(var i = 0, len = rows.length; i < len; i++){
55901             if(before){
55902                 tbody.insertBefore(rows[0], before);
55903             }else{
55904                 tbody.appendChild(rows[0]);
55905             }
55906         }
55907         b.innerHTML = "";
55908         b = null;
55909     },
55910
55911     deleteRows : function(dm, firstRow, lastRow){
55912         if(dm.getRowCount()<1){
55913             this.fireEvent("beforerefresh", this);
55914             this.mainBody.update("");
55915             this.lockedBody.update("");
55916             this.fireEvent("refresh", this);
55917         }else{
55918             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
55919             var bt = this.getBodyTable();
55920             var tbody = bt.firstChild;
55921             var rows = bt.rows;
55922             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
55923                 tbody.removeChild(rows[firstRow]);
55924             }
55925             this.stripeRows(firstRow);
55926             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
55927         }
55928     },
55929
55930     updateRows : function(dataSource, firstRow, lastRow){
55931         var s = this.getScrollState();
55932         this.refresh();
55933         this.restoreScroll(s);
55934     },
55935
55936     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
55937         if(!noRefresh){
55938            this.refresh();
55939         }
55940         this.updateHeaderSortState();
55941     },
55942
55943     getScrollState : function(){
55944         
55945         var sb = this.scroller.dom;
55946         return {left: sb.scrollLeft, top: sb.scrollTop};
55947     },
55948
55949     stripeRows : function(startRow){
55950         if(!this.grid.stripeRows || this.ds.getCount() < 1){
55951             return;
55952         }
55953         startRow = startRow || 0;
55954         var rows = this.getBodyTable().rows;
55955         var lrows = this.getLockedTable().rows;
55956         var cls = ' x-grid-row-alt ';
55957         for(var i = startRow, len = rows.length; i < len; i++){
55958             var row = rows[i], lrow = lrows[i];
55959             var isAlt = ((i+1) % 2 == 0);
55960             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
55961             if(isAlt == hasAlt){
55962                 continue;
55963             }
55964             if(isAlt){
55965                 row.className += " x-grid-row-alt";
55966             }else{
55967                 row.className = row.className.replace("x-grid-row-alt", "");
55968             }
55969             if(lrow){
55970                 lrow.className = row.className;
55971             }
55972         }
55973     },
55974
55975     restoreScroll : function(state){
55976         //Roo.log('GridView.restoreScroll');
55977         var sb = this.scroller.dom;
55978         sb.scrollLeft = state.left;
55979         sb.scrollTop = state.top;
55980         this.syncScroll();
55981     },
55982
55983     syncScroll : function(){
55984         //Roo.log('GridView.syncScroll');
55985         var sb = this.scroller.dom;
55986         var sh = this.mainHd.dom;
55987         var bs = this.mainBody.dom;
55988         var lv = this.lockedBody.dom;
55989         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
55990         lv.scrollTop = bs.scrollTop = sb.scrollTop;
55991     },
55992
55993     handleScroll : function(e){
55994         this.syncScroll();
55995         var sb = this.scroller.dom;
55996         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
55997         e.stopEvent();
55998     },
55999
56000     handleWheel : function(e){
56001         var d = e.getWheelDelta();
56002         this.scroller.dom.scrollTop -= d*22;
56003         // set this here to prevent jumpy scrolling on large tables
56004         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
56005         e.stopEvent();
56006     },
56007
56008     renderRows : function(startRow, endRow){
56009         // pull in all the crap needed to render rows
56010         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
56011         var colCount = cm.getColumnCount();
56012
56013         if(ds.getCount() < 1){
56014             return ["", ""];
56015         }
56016
56017         // build a map for all the columns
56018         var cs = [];
56019         for(var i = 0; i < colCount; i++){
56020             var name = cm.getDataIndex(i);
56021             cs[i] = {
56022                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
56023                 renderer : cm.getRenderer(i),
56024                 id : cm.getColumnId(i),
56025                 locked : cm.isLocked(i),
56026                 has_editor : cm.isCellEditable(i)
56027             };
56028         }
56029
56030         startRow = startRow || 0;
56031         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
56032
56033         // records to render
56034         var rs = ds.getRange(startRow, endRow);
56035
56036         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
56037     },
56038
56039     // As much as I hate to duplicate code, this was branched because FireFox really hates
56040     // [].join("") on strings. The performance difference was substantial enough to
56041     // branch this function
56042     doRender : Roo.isGecko ?
56043             function(cs, rs, ds, startRow, colCount, stripe){
56044                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56045                 // buffers
56046                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56047                 
56048                 var hasListener = this.grid.hasListener('rowclass');
56049                 var rowcfg = {};
56050                 for(var j = 0, len = rs.length; j < len; j++){
56051                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
56052                     for(var i = 0; i < colCount; i++){
56053                         c = cs[i];
56054                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56055                         p.id = c.id;
56056                         p.css = p.attr = "";
56057                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56058                         if(p.value == undefined || p.value === "") {
56059                             p.value = "&#160;";
56060                         }
56061                         if(c.has_editor){
56062                             p.css += ' x-grid-editable-cell';
56063                         }
56064                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
56065                             p.css +=  ' x-grid-dirty-cell';
56066                         }
56067                         var markup = ct.apply(p);
56068                         if(!c.locked){
56069                             cb+= markup;
56070                         }else{
56071                             lcb+= markup;
56072                         }
56073                     }
56074                     var alt = [];
56075                     if(stripe && ((rowIndex+1) % 2 == 0)){
56076                         alt.push("x-grid-row-alt")
56077                     }
56078                     if(r.dirty){
56079                         alt.push(  " x-grid-dirty-row");
56080                     }
56081                     rp.cells = lcb;
56082                     if(this.getRowClass){
56083                         alt.push(this.getRowClass(r, rowIndex));
56084                     }
56085                     if (hasListener) {
56086                         rowcfg = {
56087                              
56088                             record: r,
56089                             rowIndex : rowIndex,
56090                             rowClass : ''
56091                         };
56092                         this.grid.fireEvent('rowclass', this, rowcfg);
56093                         alt.push(rowcfg.rowClass);
56094                     }
56095                     rp.alt = alt.join(" ");
56096                     lbuf+= rt.apply(rp);
56097                     rp.cells = cb;
56098                     buf+=  rt.apply(rp);
56099                 }
56100                 return [lbuf, buf];
56101             } :
56102             function(cs, rs, ds, startRow, colCount, stripe){
56103                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56104                 // buffers
56105                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56106                 var hasListener = this.grid.hasListener('rowclass');
56107  
56108                 var rowcfg = {};
56109                 for(var j = 0, len = rs.length; j < len; j++){
56110                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
56111                     for(var i = 0; i < colCount; i++){
56112                         c = cs[i];
56113                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56114                         p.id = c.id;
56115                         p.css = p.attr = "";
56116                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56117                         if(p.value == undefined || p.value === "") {
56118                             p.value = "&#160;";
56119                         }
56120                         //Roo.log(c);
56121                          if(c.has_editor){
56122                             p.css += ' x-grid-editable-cell';
56123                         }
56124                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
56125                             p.css += ' x-grid-dirty-cell' 
56126                         }
56127                         
56128                         var markup = ct.apply(p);
56129                         if(!c.locked){
56130                             cb[cb.length] = markup;
56131                         }else{
56132                             lcb[lcb.length] = markup;
56133                         }
56134                     }
56135                     var alt = [];
56136                     if(stripe && ((rowIndex+1) % 2 == 0)){
56137                         alt.push( "x-grid-row-alt");
56138                     }
56139                     if(r.dirty){
56140                         alt.push(" x-grid-dirty-row");
56141                     }
56142                     rp.cells = lcb;
56143                     if(this.getRowClass){
56144                         alt.push( this.getRowClass(r, rowIndex));
56145                     }
56146                     if (hasListener) {
56147                         rowcfg = {
56148                              
56149                             record: r,
56150                             rowIndex : rowIndex,
56151                             rowClass : ''
56152                         };
56153                         this.grid.fireEvent('rowclass', this, rowcfg);
56154                         alt.push(rowcfg.rowClass);
56155                     }
56156                     
56157                     rp.alt = alt.join(" ");
56158                     rp.cells = lcb.join("");
56159                     lbuf[lbuf.length] = rt.apply(rp);
56160                     rp.cells = cb.join("");
56161                     buf[buf.length] =  rt.apply(rp);
56162                 }
56163                 return [lbuf.join(""), buf.join("")];
56164             },
56165
56166     renderBody : function(){
56167         var markup = this.renderRows();
56168         var bt = this.templates.body;
56169         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56170     },
56171
56172     /**
56173      * Refreshes the grid
56174      * @param {Boolean} headersToo
56175      */
56176     refresh : function(headersToo){
56177         this.fireEvent("beforerefresh", this);
56178         this.grid.stopEditing();
56179         var result = this.renderBody();
56180         this.lockedBody.update(result[0]);
56181         this.mainBody.update(result[1]);
56182         if(headersToo === true){
56183             this.updateHeaders();
56184             this.updateColumns();
56185             this.updateSplitters();
56186             this.updateHeaderSortState();
56187         }
56188         this.syncRowHeights();
56189         this.layout();
56190         this.fireEvent("refresh", this);
56191     },
56192
56193     handleColumnMove : function(cm, oldIndex, newIndex){
56194         this.indexMap = null;
56195         var s = this.getScrollState();
56196         this.refresh(true);
56197         this.restoreScroll(s);
56198         this.afterMove(newIndex);
56199     },
56200
56201     afterMove : function(colIndex){
56202         if(this.enableMoveAnim && Roo.enableFx){
56203             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56204         }
56205         // if multisort - fix sortOrder, and reload..
56206         if (this.grid.dataSource.multiSort) {
56207             // the we can call sort again..
56208             var dm = this.grid.dataSource;
56209             var cm = this.grid.colModel;
56210             var so = [];
56211             for(var i = 0; i < cm.config.length; i++ ) {
56212                 
56213                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56214                     continue; // dont' bother, it's not in sort list or being set.
56215                 }
56216                 
56217                 so.push(cm.config[i].dataIndex);
56218             };
56219             dm.sortOrder = so;
56220             dm.load(dm.lastOptions);
56221             
56222             
56223         }
56224         
56225     },
56226
56227     updateCell : function(dm, rowIndex, dataIndex){
56228         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56229         if(typeof colIndex == "undefined"){ // not present in grid
56230             return;
56231         }
56232         var cm = this.grid.colModel;
56233         var cell = this.getCell(rowIndex, colIndex);
56234         var cellText = this.getCellText(rowIndex, colIndex);
56235
56236         var p = {
56237             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56238             id : cm.getColumnId(colIndex),
56239             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56240         };
56241         var renderer = cm.getRenderer(colIndex);
56242         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56243         if(typeof val == "undefined" || val === "") {
56244             val = "&#160;";
56245         }
56246         cellText.innerHTML = val;
56247         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56248         this.syncRowHeights(rowIndex, rowIndex);
56249     },
56250
56251     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56252         var maxWidth = 0;
56253         if(this.grid.autoSizeHeaders){
56254             var h = this.getHeaderCellMeasure(colIndex);
56255             maxWidth = Math.max(maxWidth, h.scrollWidth);
56256         }
56257         var tb, index;
56258         if(this.cm.isLocked(colIndex)){
56259             tb = this.getLockedTable();
56260             index = colIndex;
56261         }else{
56262             tb = this.getBodyTable();
56263             index = colIndex - this.cm.getLockedCount();
56264         }
56265         if(tb && tb.rows){
56266             var rows = tb.rows;
56267             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56268             for(var i = 0; i < stopIndex; i++){
56269                 var cell = rows[i].childNodes[index].firstChild;
56270                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56271             }
56272         }
56273         return maxWidth + /*margin for error in IE*/ 5;
56274     },
56275     /**
56276      * Autofit a column to its content.
56277      * @param {Number} colIndex
56278      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56279      */
56280      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56281          if(this.cm.isHidden(colIndex)){
56282              return; // can't calc a hidden column
56283          }
56284         if(forceMinSize){
56285             var cid = this.cm.getColumnId(colIndex);
56286             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56287            if(this.grid.autoSizeHeaders){
56288                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56289            }
56290         }
56291         var newWidth = this.calcColumnWidth(colIndex);
56292         this.cm.setColumnWidth(colIndex,
56293             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56294         if(!suppressEvent){
56295             this.grid.fireEvent("columnresize", colIndex, newWidth);
56296         }
56297     },
56298
56299     /**
56300      * Autofits all columns to their content and then expands to fit any extra space in the grid
56301      */
56302      autoSizeColumns : function(){
56303         var cm = this.grid.colModel;
56304         var colCount = cm.getColumnCount();
56305         for(var i = 0; i < colCount; i++){
56306             this.autoSizeColumn(i, true, true);
56307         }
56308         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56309             this.fitColumns();
56310         }else{
56311             this.updateColumns();
56312             this.layout();
56313         }
56314     },
56315
56316     /**
56317      * Autofits all columns to the grid's width proportionate with their current size
56318      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56319      */
56320     fitColumns : function(reserveScrollSpace){
56321         var cm = this.grid.colModel;
56322         var colCount = cm.getColumnCount();
56323         var cols = [];
56324         var width = 0;
56325         var i, w;
56326         for (i = 0; i < colCount; i++){
56327             if(!cm.isHidden(i) && !cm.isFixed(i)){
56328                 w = cm.getColumnWidth(i);
56329                 cols.push(i);
56330                 cols.push(w);
56331                 width += w;
56332             }
56333         }
56334         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56335         if(reserveScrollSpace){
56336             avail -= 17;
56337         }
56338         var frac = (avail - cm.getTotalWidth())/width;
56339         while (cols.length){
56340             w = cols.pop();
56341             i = cols.pop();
56342             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56343         }
56344         this.updateColumns();
56345         this.layout();
56346     },
56347
56348     onRowSelect : function(rowIndex){
56349         var row = this.getRowComposite(rowIndex);
56350         row.addClass("x-grid-row-selected");
56351     },
56352
56353     onRowDeselect : function(rowIndex){
56354         var row = this.getRowComposite(rowIndex);
56355         row.removeClass("x-grid-row-selected");
56356     },
56357
56358     onCellSelect : function(row, col){
56359         var cell = this.getCell(row, col);
56360         if(cell){
56361             Roo.fly(cell).addClass("x-grid-cell-selected");
56362         }
56363     },
56364
56365     onCellDeselect : function(row, col){
56366         var cell = this.getCell(row, col);
56367         if(cell){
56368             Roo.fly(cell).removeClass("x-grid-cell-selected");
56369         }
56370     },
56371
56372     updateHeaderSortState : function(){
56373         
56374         // sort state can be single { field: xxx, direction : yyy}
56375         // or   { xxx=>ASC , yyy : DESC ..... }
56376         
56377         var mstate = {};
56378         if (!this.ds.multiSort) { 
56379             var state = this.ds.getSortState();
56380             if(!state){
56381                 return;
56382             }
56383             mstate[state.field] = state.direction;
56384             // FIXME... - this is not used here.. but might be elsewhere..
56385             this.sortState = state;
56386             
56387         } else {
56388             mstate = this.ds.sortToggle;
56389         }
56390         //remove existing sort classes..
56391         
56392         var sc = this.sortClasses;
56393         var hds = this.el.select(this.headerSelector).removeClass(sc);
56394         
56395         for(var f in mstate) {
56396         
56397             var sortColumn = this.cm.findColumnIndex(f);
56398             
56399             if(sortColumn != -1){
56400                 var sortDir = mstate[f];        
56401                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56402             }
56403         }
56404         
56405          
56406         
56407     },
56408
56409
56410     handleHeaderClick : function(g, index,e){
56411         
56412         Roo.log("header click");
56413         
56414         if (Roo.isTouch) {
56415             // touch events on header are handled by context
56416             this.handleHdCtx(g,index,e);
56417             return;
56418         }
56419         
56420         
56421         if(this.headersDisabled){
56422             return;
56423         }
56424         var dm = g.dataSource, cm = g.colModel;
56425         if(!cm.isSortable(index)){
56426             return;
56427         }
56428         g.stopEditing();
56429         
56430         if (dm.multiSort) {
56431             // update the sortOrder
56432             var so = [];
56433             for(var i = 0; i < cm.config.length; i++ ) {
56434                 
56435                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56436                     continue; // dont' bother, it's not in sort list or being set.
56437                 }
56438                 
56439                 so.push(cm.config[i].dataIndex);
56440             };
56441             dm.sortOrder = so;
56442         }
56443         
56444         
56445         dm.sort(cm.getDataIndex(index));
56446     },
56447
56448
56449     destroy : function(){
56450         if(this.colMenu){
56451             this.colMenu.removeAll();
56452             Roo.menu.MenuMgr.unregister(this.colMenu);
56453             this.colMenu.getEl().remove();
56454             delete this.colMenu;
56455         }
56456         if(this.hmenu){
56457             this.hmenu.removeAll();
56458             Roo.menu.MenuMgr.unregister(this.hmenu);
56459             this.hmenu.getEl().remove();
56460             delete this.hmenu;
56461         }
56462         if(this.grid.enableColumnMove){
56463             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56464             if(dds){
56465                 for(var dd in dds){
56466                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56467                         var elid = dds[dd].dragElId;
56468                         dds[dd].unreg();
56469                         Roo.get(elid).remove();
56470                     } else if(dds[dd].config.isTarget){
56471                         dds[dd].proxyTop.remove();
56472                         dds[dd].proxyBottom.remove();
56473                         dds[dd].unreg();
56474                     }
56475                     if(Roo.dd.DDM.locationCache[dd]){
56476                         delete Roo.dd.DDM.locationCache[dd];
56477                     }
56478                 }
56479                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56480             }
56481         }
56482         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56483         this.bind(null, null);
56484         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56485     },
56486
56487     handleLockChange : function(){
56488         this.refresh(true);
56489     },
56490
56491     onDenyColumnLock : function(){
56492
56493     },
56494
56495     onDenyColumnHide : function(){
56496
56497     },
56498
56499     handleHdMenuClick : function(item){
56500         var index = this.hdCtxIndex;
56501         var cm = this.cm, ds = this.ds;
56502         switch(item.id){
56503             case "asc":
56504                 ds.sort(cm.getDataIndex(index), "ASC");
56505                 break;
56506             case "desc":
56507                 ds.sort(cm.getDataIndex(index), "DESC");
56508                 break;
56509             case "lock":
56510                 var lc = cm.getLockedCount();
56511                 if(cm.getColumnCount(true) <= lc+1){
56512                     this.onDenyColumnLock();
56513                     return;
56514                 }
56515                 if(lc != index){
56516                     cm.setLocked(index, true, true);
56517                     cm.moveColumn(index, lc);
56518                     this.grid.fireEvent("columnmove", index, lc);
56519                 }else{
56520                     cm.setLocked(index, true);
56521                 }
56522             break;
56523             case "unlock":
56524                 var lc = cm.getLockedCount();
56525                 if((lc-1) != index){
56526                     cm.setLocked(index, false, true);
56527                     cm.moveColumn(index, lc-1);
56528                     this.grid.fireEvent("columnmove", index, lc-1);
56529                 }else{
56530                     cm.setLocked(index, false);
56531                 }
56532             break;
56533             case 'wider': // used to expand cols on touch..
56534             case 'narrow':
56535                 var cw = cm.getColumnWidth(index);
56536                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56537                 cw = Math.max(0, cw);
56538                 cw = Math.min(cw,4000);
56539                 cm.setColumnWidth(index, cw);
56540                 break;
56541                 
56542             default:
56543                 index = cm.getIndexById(item.id.substr(4));
56544                 if(index != -1){
56545                     if(item.checked && cm.getColumnCount(true) <= 1){
56546                         this.onDenyColumnHide();
56547                         return false;
56548                     }
56549                     cm.setHidden(index, item.checked);
56550                 }
56551         }
56552         return true;
56553     },
56554
56555     beforeColMenuShow : function(){
56556         var cm = this.cm,  colCount = cm.getColumnCount();
56557         this.colMenu.removeAll();
56558         for(var i = 0; i < colCount; i++){
56559             this.colMenu.add(new Roo.menu.CheckItem({
56560                 id: "col-"+cm.getColumnId(i),
56561                 text: cm.getColumnHeader(i),
56562                 checked: !cm.isHidden(i),
56563                 hideOnClick:false
56564             }));
56565         }
56566     },
56567
56568     handleHdCtx : function(g, index, e){
56569         e.stopEvent();
56570         var hd = this.getHeaderCell(index);
56571         this.hdCtxIndex = index;
56572         var ms = this.hmenu.items, cm = this.cm;
56573         ms.get("asc").setDisabled(!cm.isSortable(index));
56574         ms.get("desc").setDisabled(!cm.isSortable(index));
56575         if(this.grid.enableColLock !== false){
56576             ms.get("lock").setDisabled(cm.isLocked(index));
56577             ms.get("unlock").setDisabled(!cm.isLocked(index));
56578         }
56579         this.hmenu.show(hd, "tl-bl");
56580     },
56581
56582     handleHdOver : function(e){
56583         var hd = this.findHeaderCell(e.getTarget());
56584         if(hd && !this.headersDisabled){
56585             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56586                this.fly(hd).addClass("x-grid-hd-over");
56587             }
56588         }
56589     },
56590
56591     handleHdOut : function(e){
56592         var hd = this.findHeaderCell(e.getTarget());
56593         if(hd){
56594             this.fly(hd).removeClass("x-grid-hd-over");
56595         }
56596     },
56597
56598     handleSplitDblClick : function(e, t){
56599         var i = this.getCellIndex(t);
56600         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56601             this.autoSizeColumn(i, true);
56602             this.layout();
56603         }
56604     },
56605
56606     render : function(){
56607
56608         var cm = this.cm;
56609         var colCount = cm.getColumnCount();
56610
56611         if(this.grid.monitorWindowResize === true){
56612             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56613         }
56614         var header = this.renderHeaders();
56615         var body = this.templates.body.apply({rows:""});
56616         var html = this.templates.master.apply({
56617             lockedBody: body,
56618             body: body,
56619             lockedHeader: header[0],
56620             header: header[1]
56621         });
56622
56623         //this.updateColumns();
56624
56625         this.grid.getGridEl().dom.innerHTML = html;
56626
56627         this.initElements();
56628         
56629         // a kludge to fix the random scolling effect in webkit
56630         this.el.on("scroll", function() {
56631             this.el.dom.scrollTop=0; // hopefully not recursive..
56632         },this);
56633
56634         this.scroller.on("scroll", this.handleScroll, this);
56635         this.lockedBody.on("mousewheel", this.handleWheel, this);
56636         this.mainBody.on("mousewheel", this.handleWheel, this);
56637
56638         this.mainHd.on("mouseover", this.handleHdOver, this);
56639         this.mainHd.on("mouseout", this.handleHdOut, this);
56640         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56641                 {delegate: "."+this.splitClass});
56642
56643         this.lockedHd.on("mouseover", this.handleHdOver, this);
56644         this.lockedHd.on("mouseout", this.handleHdOut, this);
56645         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
56646                 {delegate: "."+this.splitClass});
56647
56648         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
56649             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56650         }
56651
56652         this.updateSplitters();
56653
56654         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
56655             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56656             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56657         }
56658
56659         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
56660             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
56661             this.hmenu.add(
56662                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
56663                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
56664             );
56665             if(this.grid.enableColLock !== false){
56666                 this.hmenu.add('-',
56667                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
56668                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
56669                 );
56670             }
56671             if (Roo.isTouch) {
56672                  this.hmenu.add('-',
56673                     {id:"wider", text: this.columnsWiderText},
56674                     {id:"narrow", text: this.columnsNarrowText }
56675                 );
56676                 
56677                  
56678             }
56679             
56680             if(this.grid.enableColumnHide !== false){
56681
56682                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
56683                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
56684                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
56685
56686                 this.hmenu.add('-',
56687                     {id:"columns", text: this.columnsText, menu: this.colMenu}
56688                 );
56689             }
56690             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
56691
56692             this.grid.on("headercontextmenu", this.handleHdCtx, this);
56693         }
56694
56695         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
56696             this.dd = new Roo.grid.GridDragZone(this.grid, {
56697                 ddGroup : this.grid.ddGroup || 'GridDD'
56698             });
56699             
56700         }
56701
56702         /*
56703         for(var i = 0; i < colCount; i++){
56704             if(cm.isHidden(i)){
56705                 this.hideColumn(i);
56706             }
56707             if(cm.config[i].align){
56708                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
56709                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
56710             }
56711         }*/
56712         
56713         this.updateHeaderSortState();
56714
56715         this.beforeInitialResize();
56716         this.layout(true);
56717
56718         // two part rendering gives faster view to the user
56719         this.renderPhase2.defer(1, this);
56720     },
56721
56722     renderPhase2 : function(){
56723         // render the rows now
56724         this.refresh();
56725         if(this.grid.autoSizeColumns){
56726             this.autoSizeColumns();
56727         }
56728     },
56729
56730     beforeInitialResize : function(){
56731
56732     },
56733
56734     onColumnSplitterMoved : function(i, w){
56735         this.userResized = true;
56736         var cm = this.grid.colModel;
56737         cm.setColumnWidth(i, w, true);
56738         var cid = cm.getColumnId(i);
56739         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56740         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56741         this.updateSplitters();
56742         this.layout();
56743         this.grid.fireEvent("columnresize", i, w);
56744     },
56745
56746     syncRowHeights : function(startIndex, endIndex){
56747         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
56748             startIndex = startIndex || 0;
56749             var mrows = this.getBodyTable().rows;
56750             var lrows = this.getLockedTable().rows;
56751             var len = mrows.length-1;
56752             endIndex = Math.min(endIndex || len, len);
56753             for(var i = startIndex; i <= endIndex; i++){
56754                 var m = mrows[i], l = lrows[i];
56755                 var h = Math.max(m.offsetHeight, l.offsetHeight);
56756                 m.style.height = l.style.height = h + "px";
56757             }
56758         }
56759     },
56760
56761     layout : function(initialRender, is2ndPass){
56762         var g = this.grid;
56763         var auto = g.autoHeight;
56764         var scrollOffset = 16;
56765         var c = g.getGridEl(), cm = this.cm,
56766                 expandCol = g.autoExpandColumn,
56767                 gv = this;
56768         //c.beginMeasure();
56769
56770         if(!c.dom.offsetWidth){ // display:none?
56771             if(initialRender){
56772                 this.lockedWrap.show();
56773                 this.mainWrap.show();
56774             }
56775             return;
56776         }
56777
56778         var hasLock = this.cm.isLocked(0);
56779
56780         var tbh = this.headerPanel.getHeight();
56781         var bbh = this.footerPanel.getHeight();
56782
56783         if(auto){
56784             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
56785             var newHeight = ch + c.getBorderWidth("tb");
56786             if(g.maxHeight){
56787                 newHeight = Math.min(g.maxHeight, newHeight);
56788             }
56789             c.setHeight(newHeight);
56790         }
56791
56792         if(g.autoWidth){
56793             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
56794         }
56795
56796         var s = this.scroller;
56797
56798         var csize = c.getSize(true);
56799
56800         this.el.setSize(csize.width, csize.height);
56801
56802         this.headerPanel.setWidth(csize.width);
56803         this.footerPanel.setWidth(csize.width);
56804
56805         var hdHeight = this.mainHd.getHeight();
56806         var vw = csize.width;
56807         var vh = csize.height - (tbh + bbh);
56808
56809         s.setSize(vw, vh);
56810
56811         var bt = this.getBodyTable();
56812         
56813         if(cm.getLockedCount() == cm.config.length){
56814             bt = this.getLockedTable();
56815         }
56816         
56817         var ltWidth = hasLock ?
56818                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
56819
56820         var scrollHeight = bt.offsetHeight;
56821         var scrollWidth = ltWidth + bt.offsetWidth;
56822         var vscroll = false, hscroll = false;
56823
56824         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
56825
56826         var lw = this.lockedWrap, mw = this.mainWrap;
56827         var lb = this.lockedBody, mb = this.mainBody;
56828
56829         setTimeout(function(){
56830             var t = s.dom.offsetTop;
56831             var w = s.dom.clientWidth,
56832                 h = s.dom.clientHeight;
56833
56834             lw.setTop(t);
56835             lw.setSize(ltWidth, h);
56836
56837             mw.setLeftTop(ltWidth, t);
56838             mw.setSize(w-ltWidth, h);
56839
56840             lb.setHeight(h-hdHeight);
56841             mb.setHeight(h-hdHeight);
56842
56843             if(is2ndPass !== true && !gv.userResized && expandCol){
56844                 // high speed resize without full column calculation
56845                 
56846                 var ci = cm.getIndexById(expandCol);
56847                 if (ci < 0) {
56848                     ci = cm.findColumnIndex(expandCol);
56849                 }
56850                 ci = Math.max(0, ci); // make sure it's got at least the first col.
56851                 var expandId = cm.getColumnId(ci);
56852                 var  tw = cm.getTotalWidth(false);
56853                 var currentWidth = cm.getColumnWidth(ci);
56854                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
56855                 if(currentWidth != cw){
56856                     cm.setColumnWidth(ci, cw, true);
56857                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56858                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56859                     gv.updateSplitters();
56860                     gv.layout(false, true);
56861                 }
56862             }
56863
56864             if(initialRender){
56865                 lw.show();
56866                 mw.show();
56867             }
56868             //c.endMeasure();
56869         }, 10);
56870     },
56871
56872     onWindowResize : function(){
56873         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
56874             return;
56875         }
56876         this.layout();
56877     },
56878
56879     appendFooter : function(parentEl){
56880         return null;
56881     },
56882
56883     sortAscText : "Sort Ascending",
56884     sortDescText : "Sort Descending",
56885     lockText : "Lock Column",
56886     unlockText : "Unlock Column",
56887     columnsText : "Columns",
56888  
56889     columnsWiderText : "Wider",
56890     columnsNarrowText : "Thinner"
56891 });
56892
56893
56894 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
56895     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
56896     this.proxy.el.addClass('x-grid3-col-dd');
56897 };
56898
56899 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
56900     handleMouseDown : function(e){
56901
56902     },
56903
56904     callHandleMouseDown : function(e){
56905         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
56906     }
56907 });
56908 /*
56909  * Based on:
56910  * Ext JS Library 1.1.1
56911  * Copyright(c) 2006-2007, Ext JS, LLC.
56912  *
56913  * Originally Released Under LGPL - original licence link has changed is not relivant.
56914  *
56915  * Fork - LGPL
56916  * <script type="text/javascript">
56917  */
56918  
56919 // private
56920 // This is a support class used internally by the Grid components
56921 Roo.grid.SplitDragZone = function(grid, hd, hd2){
56922     this.grid = grid;
56923     this.view = grid.getView();
56924     this.proxy = this.view.resizeProxy;
56925     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
56926         "gridSplitters" + this.grid.getGridEl().id, {
56927         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
56928     });
56929     this.setHandleElId(Roo.id(hd));
56930     this.setOuterHandleElId(Roo.id(hd2));
56931     this.scroll = false;
56932 };
56933 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
56934     fly: Roo.Element.fly,
56935
56936     b4StartDrag : function(x, y){
56937         this.view.headersDisabled = true;
56938         this.proxy.setHeight(this.view.mainWrap.getHeight());
56939         var w = this.cm.getColumnWidth(this.cellIndex);
56940         var minw = Math.max(w-this.grid.minColumnWidth, 0);
56941         this.resetConstraints();
56942         this.setXConstraint(minw, 1000);
56943         this.setYConstraint(0, 0);
56944         this.minX = x - minw;
56945         this.maxX = x + 1000;
56946         this.startPos = x;
56947         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
56948     },
56949
56950
56951     handleMouseDown : function(e){
56952         ev = Roo.EventObject.setEvent(e);
56953         var t = this.fly(ev.getTarget());
56954         if(t.hasClass("x-grid-split")){
56955             this.cellIndex = this.view.getCellIndex(t.dom);
56956             this.split = t.dom;
56957             this.cm = this.grid.colModel;
56958             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
56959                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
56960             }
56961         }
56962     },
56963
56964     endDrag : function(e){
56965         this.view.headersDisabled = false;
56966         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
56967         var diff = endX - this.startPos;
56968         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
56969     },
56970
56971     autoOffset : function(){
56972         this.setDelta(0,0);
56973     }
56974 });/*
56975  * Based on:
56976  * Ext JS Library 1.1.1
56977  * Copyright(c) 2006-2007, Ext JS, LLC.
56978  *
56979  * Originally Released Under LGPL - original licence link has changed is not relivant.
56980  *
56981  * Fork - LGPL
56982  * <script type="text/javascript">
56983  */
56984  
56985 // private
56986 // This is a support class used internally by the Grid components
56987 Roo.grid.GridDragZone = function(grid, config){
56988     this.view = grid.getView();
56989     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
56990     if(this.view.lockedBody){
56991         this.setHandleElId(Roo.id(this.view.mainBody.dom));
56992         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
56993     }
56994     this.scroll = false;
56995     this.grid = grid;
56996     this.ddel = document.createElement('div');
56997     this.ddel.className = 'x-grid-dd-wrap';
56998 };
56999
57000 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
57001     ddGroup : "GridDD",
57002
57003     getDragData : function(e){
57004         var t = Roo.lib.Event.getTarget(e);
57005         var rowIndex = this.view.findRowIndex(t);
57006         var sm = this.grid.selModel;
57007             
57008         //Roo.log(rowIndex);
57009         
57010         if (sm.getSelectedCell) {
57011             // cell selection..
57012             if (!sm.getSelectedCell()) {
57013                 return false;
57014             }
57015             if (rowIndex != sm.getSelectedCell()[0]) {
57016                 return false;
57017             }
57018         
57019         }
57020         
57021         if(rowIndex !== false){
57022             
57023             // if editorgrid.. 
57024             
57025             
57026             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
57027                
57028             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
57029               //  
57030             //}
57031             if (e.hasModifier()){
57032                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
57033             }
57034             
57035             Roo.log("getDragData");
57036             
57037             return {
57038                 grid: this.grid,
57039                 ddel: this.ddel,
57040                 rowIndex: rowIndex,
57041                 selections:sm.getSelections ? sm.getSelections() : (
57042                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
57043                 )
57044             };
57045         }
57046         return false;
57047     },
57048
57049     onInitDrag : function(e){
57050         var data = this.dragData;
57051         this.ddel.innerHTML = this.grid.getDragDropText();
57052         this.proxy.update(this.ddel);
57053         // fire start drag?
57054     },
57055
57056     afterRepair : function(){
57057         this.dragging = false;
57058     },
57059
57060     getRepairXY : function(e, data){
57061         return false;
57062     },
57063
57064     onEndDrag : function(data, e){
57065         // fire end drag?
57066     },
57067
57068     onValidDrop : function(dd, e, id){
57069         // fire drag drop?
57070         this.hideProxy();
57071     },
57072
57073     beforeInvalidDrop : function(e, id){
57074
57075     }
57076 });/*
57077  * Based on:
57078  * Ext JS Library 1.1.1
57079  * Copyright(c) 2006-2007, Ext JS, LLC.
57080  *
57081  * Originally Released Under LGPL - original licence link has changed is not relivant.
57082  *
57083  * Fork - LGPL
57084  * <script type="text/javascript">
57085  */
57086  
57087
57088 /**
57089  * @class Roo.grid.ColumnModel
57090  * @extends Roo.util.Observable
57091  * This is the default implementation of a ColumnModel used by the Grid. It defines
57092  * the columns in the grid.
57093  * <br>Usage:<br>
57094  <pre><code>
57095  var colModel = new Roo.grid.ColumnModel([
57096         {header: "Ticker", width: 60, sortable: true, locked: true},
57097         {header: "Company Name", width: 150, sortable: true},
57098         {header: "Market Cap.", width: 100, sortable: true},
57099         {header: "$ Sales", width: 100, sortable: true, renderer: money},
57100         {header: "Employees", width: 100, sortable: true, resizable: false}
57101  ]);
57102  </code></pre>
57103  * <p>
57104  
57105  * The config options listed for this class are options which may appear in each
57106  * individual column definition.
57107  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
57108  * @constructor
57109  * @param {Object} config An Array of column config objects. See this class's
57110  * config objects for details.
57111 */
57112 Roo.grid.ColumnModel = function(config){
57113         /**
57114      * The config passed into the constructor
57115      */
57116     this.config = config;
57117     this.lookup = {};
57118
57119     // if no id, create one
57120     // if the column does not have a dataIndex mapping,
57121     // map it to the order it is in the config
57122     for(var i = 0, len = config.length; i < len; i++){
57123         var c = config[i];
57124         if(typeof c.dataIndex == "undefined"){
57125             c.dataIndex = i;
57126         }
57127         if(typeof c.renderer == "string"){
57128             c.renderer = Roo.util.Format[c.renderer];
57129         }
57130         if(typeof c.id == "undefined"){
57131             c.id = Roo.id();
57132         }
57133         if(c.editor && c.editor.xtype){
57134             c.editor  = Roo.factory(c.editor, Roo.grid);
57135         }
57136         if(c.editor && c.editor.isFormField){
57137             c.editor = new Roo.grid.GridEditor(c.editor);
57138         }
57139         this.lookup[c.id] = c;
57140     }
57141
57142     /**
57143      * The width of columns which have no width specified (defaults to 100)
57144      * @type Number
57145      */
57146     this.defaultWidth = 100;
57147
57148     /**
57149      * Default sortable of columns which have no sortable specified (defaults to false)
57150      * @type Boolean
57151      */
57152     this.defaultSortable = false;
57153
57154     this.addEvents({
57155         /**
57156              * @event widthchange
57157              * Fires when the width of a column changes.
57158              * @param {ColumnModel} this
57159              * @param {Number} columnIndex The column index
57160              * @param {Number} newWidth The new width
57161              */
57162             "widthchange": true,
57163         /**
57164              * @event headerchange
57165              * Fires when the text of a header changes.
57166              * @param {ColumnModel} this
57167              * @param {Number} columnIndex The column index
57168              * @param {Number} newText The new header text
57169              */
57170             "headerchange": true,
57171         /**
57172              * @event hiddenchange
57173              * Fires when a column is hidden or "unhidden".
57174              * @param {ColumnModel} this
57175              * @param {Number} columnIndex The column index
57176              * @param {Boolean} hidden true if hidden, false otherwise
57177              */
57178             "hiddenchange": true,
57179             /**
57180          * @event columnmoved
57181          * Fires when a column is moved.
57182          * @param {ColumnModel} this
57183          * @param {Number} oldIndex
57184          * @param {Number} newIndex
57185          */
57186         "columnmoved" : true,
57187         /**
57188          * @event columlockchange
57189          * Fires when a column's locked state is changed
57190          * @param {ColumnModel} this
57191          * @param {Number} colIndex
57192          * @param {Boolean} locked true if locked
57193          */
57194         "columnlockchange" : true
57195     });
57196     Roo.grid.ColumnModel.superclass.constructor.call(this);
57197 };
57198 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57199     /**
57200      * @cfg {String} header The header text to display in the Grid view.
57201      */
57202     /**
57203      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57204      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57205      * specified, the column's index is used as an index into the Record's data Array.
57206      */
57207     /**
57208      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57209      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57210      */
57211     /**
57212      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57213      * Defaults to the value of the {@link #defaultSortable} property.
57214      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57215      */
57216     /**
57217      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57218      */
57219     /**
57220      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57221      */
57222     /**
57223      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57224      */
57225     /**
57226      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57227      */
57228     /**
57229      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57230      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57231      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57232      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57233      */
57234        /**
57235      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57236      */
57237     /**
57238      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57239      */
57240     /**
57241      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
57242      */
57243     /**
57244      * @cfg {String} cursor (Optional)
57245      */
57246     /**
57247      * @cfg {String} tooltip (Optional)
57248      */
57249     /**
57250      * @cfg {Number} xs (Optional)
57251      */
57252     /**
57253      * @cfg {Number} sm (Optional)
57254      */
57255     /**
57256      * @cfg {Number} md (Optional)
57257      */
57258     /**
57259      * @cfg {Number} lg (Optional)
57260      */
57261     /**
57262      * Returns the id of the column at the specified index.
57263      * @param {Number} index The column index
57264      * @return {String} the id
57265      */
57266     getColumnId : function(index){
57267         return this.config[index].id;
57268     },
57269
57270     /**
57271      * Returns the column for a specified id.
57272      * @param {String} id The column id
57273      * @return {Object} the column
57274      */
57275     getColumnById : function(id){
57276         return this.lookup[id];
57277     },
57278
57279     
57280     /**
57281      * Returns the column for a specified dataIndex.
57282      * @param {String} dataIndex The column dataIndex
57283      * @return {Object|Boolean} the column or false if not found
57284      */
57285     getColumnByDataIndex: function(dataIndex){
57286         var index = this.findColumnIndex(dataIndex);
57287         return index > -1 ? this.config[index] : false;
57288     },
57289     
57290     /**
57291      * Returns the index for a specified column id.
57292      * @param {String} id The column id
57293      * @return {Number} the index, or -1 if not found
57294      */
57295     getIndexById : function(id){
57296         for(var i = 0, len = this.config.length; i < len; i++){
57297             if(this.config[i].id == id){
57298                 return i;
57299             }
57300         }
57301         return -1;
57302     },
57303     
57304     /**
57305      * Returns the index for a specified column dataIndex.
57306      * @param {String} dataIndex The column dataIndex
57307      * @return {Number} the index, or -1 if not found
57308      */
57309     
57310     findColumnIndex : function(dataIndex){
57311         for(var i = 0, len = this.config.length; i < len; i++){
57312             if(this.config[i].dataIndex == dataIndex){
57313                 return i;
57314             }
57315         }
57316         return -1;
57317     },
57318     
57319     
57320     moveColumn : function(oldIndex, newIndex){
57321         var c = this.config[oldIndex];
57322         this.config.splice(oldIndex, 1);
57323         this.config.splice(newIndex, 0, c);
57324         this.dataMap = null;
57325         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57326     },
57327
57328     isLocked : function(colIndex){
57329         return this.config[colIndex].locked === true;
57330     },
57331
57332     setLocked : function(colIndex, value, suppressEvent){
57333         if(this.isLocked(colIndex) == value){
57334             return;
57335         }
57336         this.config[colIndex].locked = value;
57337         if(!suppressEvent){
57338             this.fireEvent("columnlockchange", this, colIndex, value);
57339         }
57340     },
57341
57342     getTotalLockedWidth : function(){
57343         var totalWidth = 0;
57344         for(var i = 0; i < this.config.length; i++){
57345             if(this.isLocked(i) && !this.isHidden(i)){
57346                 this.totalWidth += this.getColumnWidth(i);
57347             }
57348         }
57349         return totalWidth;
57350     },
57351
57352     getLockedCount : function(){
57353         for(var i = 0, len = this.config.length; i < len; i++){
57354             if(!this.isLocked(i)){
57355                 return i;
57356             }
57357         }
57358         
57359         return this.config.length;
57360     },
57361
57362     /**
57363      * Returns the number of columns.
57364      * @return {Number}
57365      */
57366     getColumnCount : function(visibleOnly){
57367         if(visibleOnly === true){
57368             var c = 0;
57369             for(var i = 0, len = this.config.length; i < len; i++){
57370                 if(!this.isHidden(i)){
57371                     c++;
57372                 }
57373             }
57374             return c;
57375         }
57376         return this.config.length;
57377     },
57378
57379     /**
57380      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57381      * @param {Function} fn
57382      * @param {Object} scope (optional)
57383      * @return {Array} result
57384      */
57385     getColumnsBy : function(fn, scope){
57386         var r = [];
57387         for(var i = 0, len = this.config.length; i < len; i++){
57388             var c = this.config[i];
57389             if(fn.call(scope||this, c, i) === true){
57390                 r[r.length] = c;
57391             }
57392         }
57393         return r;
57394     },
57395
57396     /**
57397      * Returns true if the specified column is sortable.
57398      * @param {Number} col The column index
57399      * @return {Boolean}
57400      */
57401     isSortable : function(col){
57402         if(typeof this.config[col].sortable == "undefined"){
57403             return this.defaultSortable;
57404         }
57405         return this.config[col].sortable;
57406     },
57407
57408     /**
57409      * Returns the rendering (formatting) function defined for the column.
57410      * @param {Number} col The column index.
57411      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57412      */
57413     getRenderer : function(col){
57414         if(!this.config[col].renderer){
57415             return Roo.grid.ColumnModel.defaultRenderer;
57416         }
57417         return this.config[col].renderer;
57418     },
57419
57420     /**
57421      * Sets the rendering (formatting) function for a column.
57422      * @param {Number} col The column index
57423      * @param {Function} fn The function to use to process the cell's raw data
57424      * to return HTML markup for the grid view. The render function is called with
57425      * the following parameters:<ul>
57426      * <li>Data value.</li>
57427      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57428      * <li>css A CSS style string to apply to the table cell.</li>
57429      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57430      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57431      * <li>Row index</li>
57432      * <li>Column index</li>
57433      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57434      */
57435     setRenderer : function(col, fn){
57436         this.config[col].renderer = fn;
57437     },
57438
57439     /**
57440      * Returns the width for the specified column.
57441      * @param {Number} col The column index
57442      * @return {Number}
57443      */
57444     getColumnWidth : function(col){
57445         return this.config[col].width * 1 || this.defaultWidth;
57446     },
57447
57448     /**
57449      * Sets the width for a column.
57450      * @param {Number} col The column index
57451      * @param {Number} width The new width
57452      */
57453     setColumnWidth : function(col, width, suppressEvent){
57454         this.config[col].width = width;
57455         this.totalWidth = null;
57456         if(!suppressEvent){
57457              this.fireEvent("widthchange", this, col, width);
57458         }
57459     },
57460
57461     /**
57462      * Returns the total width of all columns.
57463      * @param {Boolean} includeHidden True to include hidden column widths
57464      * @return {Number}
57465      */
57466     getTotalWidth : function(includeHidden){
57467         if(!this.totalWidth){
57468             this.totalWidth = 0;
57469             for(var i = 0, len = this.config.length; i < len; i++){
57470                 if(includeHidden || !this.isHidden(i)){
57471                     this.totalWidth += this.getColumnWidth(i);
57472                 }
57473             }
57474         }
57475         return this.totalWidth;
57476     },
57477
57478     /**
57479      * Returns the header for the specified column.
57480      * @param {Number} col The column index
57481      * @return {String}
57482      */
57483     getColumnHeader : function(col){
57484         return this.config[col].header;
57485     },
57486
57487     /**
57488      * Sets the header for a column.
57489      * @param {Number} col The column index
57490      * @param {String} header The new header
57491      */
57492     setColumnHeader : function(col, header){
57493         this.config[col].header = header;
57494         this.fireEvent("headerchange", this, col, header);
57495     },
57496
57497     /**
57498      * Returns the tooltip for the specified column.
57499      * @param {Number} col The column index
57500      * @return {String}
57501      */
57502     getColumnTooltip : function(col){
57503             return this.config[col].tooltip;
57504     },
57505     /**
57506      * Sets the tooltip for a column.
57507      * @param {Number} col The column index
57508      * @param {String} tooltip The new tooltip
57509      */
57510     setColumnTooltip : function(col, tooltip){
57511             this.config[col].tooltip = tooltip;
57512     },
57513
57514     /**
57515      * Returns the dataIndex for the specified column.
57516      * @param {Number} col The column index
57517      * @return {Number}
57518      */
57519     getDataIndex : function(col){
57520         return this.config[col].dataIndex;
57521     },
57522
57523     /**
57524      * Sets the dataIndex for a column.
57525      * @param {Number} col The column index
57526      * @param {Number} dataIndex The new dataIndex
57527      */
57528     setDataIndex : function(col, dataIndex){
57529         this.config[col].dataIndex = dataIndex;
57530     },
57531
57532     
57533     
57534     /**
57535      * Returns true if the cell is editable.
57536      * @param {Number} colIndex The column index
57537      * @param {Number} rowIndex The row index - this is nto actually used..?
57538      * @return {Boolean}
57539      */
57540     isCellEditable : function(colIndex, rowIndex){
57541         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57542     },
57543
57544     /**
57545      * Returns the editor defined for the cell/column.
57546      * return false or null to disable editing.
57547      * @param {Number} colIndex The column index
57548      * @param {Number} rowIndex The row index
57549      * @return {Object}
57550      */
57551     getCellEditor : function(colIndex, rowIndex){
57552         return this.config[colIndex].editor;
57553     },
57554
57555     /**
57556      * Sets if a column is editable.
57557      * @param {Number} col The column index
57558      * @param {Boolean} editable True if the column is editable
57559      */
57560     setEditable : function(col, editable){
57561         this.config[col].editable = editable;
57562     },
57563
57564
57565     /**
57566      * Returns true if the column is hidden.
57567      * @param {Number} colIndex The column index
57568      * @return {Boolean}
57569      */
57570     isHidden : function(colIndex){
57571         return this.config[colIndex].hidden;
57572     },
57573
57574
57575     /**
57576      * Returns true if the column width cannot be changed
57577      */
57578     isFixed : function(colIndex){
57579         return this.config[colIndex].fixed;
57580     },
57581
57582     /**
57583      * Returns true if the column can be resized
57584      * @return {Boolean}
57585      */
57586     isResizable : function(colIndex){
57587         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57588     },
57589     /**
57590      * Sets if a column is hidden.
57591      * @param {Number} colIndex The column index
57592      * @param {Boolean} hidden True if the column is hidden
57593      */
57594     setHidden : function(colIndex, hidden){
57595         this.config[colIndex].hidden = hidden;
57596         this.totalWidth = null;
57597         this.fireEvent("hiddenchange", this, colIndex, hidden);
57598     },
57599
57600     /**
57601      * Sets the editor for a column.
57602      * @param {Number} col The column index
57603      * @param {Object} editor The editor object
57604      */
57605     setEditor : function(col, editor){
57606         this.config[col].editor = editor;
57607     }
57608 });
57609
57610 Roo.grid.ColumnModel.defaultRenderer = function(value)
57611 {
57612     if(typeof value == "object") {
57613         return value;
57614     }
57615         if(typeof value == "string" && value.length < 1){
57616             return "&#160;";
57617         }
57618     
57619         return String.format("{0}", value);
57620 };
57621
57622 // Alias for backwards compatibility
57623 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57624 /*
57625  * Based on:
57626  * Ext JS Library 1.1.1
57627  * Copyright(c) 2006-2007, Ext JS, LLC.
57628  *
57629  * Originally Released Under LGPL - original licence link has changed is not relivant.
57630  *
57631  * Fork - LGPL
57632  * <script type="text/javascript">
57633  */
57634
57635 /**
57636  * @class Roo.grid.AbstractSelectionModel
57637  * @extends Roo.util.Observable
57638  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57639  * implemented by descendant classes.  This class should not be directly instantiated.
57640  * @constructor
57641  */
57642 Roo.grid.AbstractSelectionModel = function(){
57643     this.locked = false;
57644     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
57645 };
57646
57647 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
57648     /** @ignore Called by the grid automatically. Do not call directly. */
57649     init : function(grid){
57650         this.grid = grid;
57651         this.initEvents();
57652     },
57653
57654     /**
57655      * Locks the selections.
57656      */
57657     lock : function(){
57658         this.locked = true;
57659     },
57660
57661     /**
57662      * Unlocks the selections.
57663      */
57664     unlock : function(){
57665         this.locked = false;
57666     },
57667
57668     /**
57669      * Returns true if the selections are locked.
57670      * @return {Boolean}
57671      */
57672     isLocked : function(){
57673         return this.locked;
57674     }
57675 });/*
57676  * Based on:
57677  * Ext JS Library 1.1.1
57678  * Copyright(c) 2006-2007, Ext JS, LLC.
57679  *
57680  * Originally Released Under LGPL - original licence link has changed is not relivant.
57681  *
57682  * Fork - LGPL
57683  * <script type="text/javascript">
57684  */
57685 /**
57686  * @extends Roo.grid.AbstractSelectionModel
57687  * @class Roo.grid.RowSelectionModel
57688  * The default SelectionModel used by {@link Roo.grid.Grid}.
57689  * It supports multiple selections and keyboard selection/navigation. 
57690  * @constructor
57691  * @param {Object} config
57692  */
57693 Roo.grid.RowSelectionModel = function(config){
57694     Roo.apply(this, config);
57695     this.selections = new Roo.util.MixedCollection(false, function(o){
57696         return o.id;
57697     });
57698
57699     this.last = false;
57700     this.lastActive = false;
57701
57702     this.addEvents({
57703         /**
57704              * @event selectionchange
57705              * Fires when the selection changes
57706              * @param {SelectionModel} this
57707              */
57708             "selectionchange" : true,
57709         /**
57710              * @event afterselectionchange
57711              * Fires after the selection changes (eg. by key press or clicking)
57712              * @param {SelectionModel} this
57713              */
57714             "afterselectionchange" : true,
57715         /**
57716              * @event beforerowselect
57717              * Fires when a row is selected being selected, return false to cancel.
57718              * @param {SelectionModel} this
57719              * @param {Number} rowIndex The selected index
57720              * @param {Boolean} keepExisting False if other selections will be cleared
57721              */
57722             "beforerowselect" : true,
57723         /**
57724              * @event rowselect
57725              * Fires when a row is selected.
57726              * @param {SelectionModel} this
57727              * @param {Number} rowIndex The selected index
57728              * @param {Roo.data.Record} r The record
57729              */
57730             "rowselect" : true,
57731         /**
57732              * @event rowdeselect
57733              * Fires when a row is deselected.
57734              * @param {SelectionModel} this
57735              * @param {Number} rowIndex The selected index
57736              */
57737         "rowdeselect" : true
57738     });
57739     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
57740     this.locked = false;
57741 };
57742
57743 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
57744     /**
57745      * @cfg {Boolean} singleSelect
57746      * True to allow selection of only one row at a time (defaults to false)
57747      */
57748     singleSelect : false,
57749
57750     // private
57751     initEvents : function(){
57752
57753         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
57754             this.grid.on("mousedown", this.handleMouseDown, this);
57755         }else{ // allow click to work like normal
57756             this.grid.on("rowclick", this.handleDragableRowClick, this);
57757         }
57758
57759         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
57760             "up" : function(e){
57761                 if(!e.shiftKey){
57762                     this.selectPrevious(e.shiftKey);
57763                 }else if(this.last !== false && this.lastActive !== false){
57764                     var last = this.last;
57765                     this.selectRange(this.last,  this.lastActive-1);
57766                     this.grid.getView().focusRow(this.lastActive);
57767                     if(last !== false){
57768                         this.last = last;
57769                     }
57770                 }else{
57771                     this.selectFirstRow();
57772                 }
57773                 this.fireEvent("afterselectionchange", this);
57774             },
57775             "down" : function(e){
57776                 if(!e.shiftKey){
57777                     this.selectNext(e.shiftKey);
57778                 }else if(this.last !== false && this.lastActive !== false){
57779                     var last = this.last;
57780                     this.selectRange(this.last,  this.lastActive+1);
57781                     this.grid.getView().focusRow(this.lastActive);
57782                     if(last !== false){
57783                         this.last = last;
57784                     }
57785                 }else{
57786                     this.selectFirstRow();
57787                 }
57788                 this.fireEvent("afterselectionchange", this);
57789             },
57790             scope: this
57791         });
57792
57793         var view = this.grid.view;
57794         view.on("refresh", this.onRefresh, this);
57795         view.on("rowupdated", this.onRowUpdated, this);
57796         view.on("rowremoved", this.onRemove, this);
57797     },
57798
57799     // private
57800     onRefresh : function(){
57801         var ds = this.grid.dataSource, i, v = this.grid.view;
57802         var s = this.selections;
57803         s.each(function(r){
57804             if((i = ds.indexOfId(r.id)) != -1){
57805                 v.onRowSelect(i);
57806                 s.add(ds.getAt(i)); // updating the selection relate data
57807             }else{
57808                 s.remove(r);
57809             }
57810         });
57811     },
57812
57813     // private
57814     onRemove : function(v, index, r){
57815         this.selections.remove(r);
57816     },
57817
57818     // private
57819     onRowUpdated : function(v, index, r){
57820         if(this.isSelected(r)){
57821             v.onRowSelect(index);
57822         }
57823     },
57824
57825     /**
57826      * Select records.
57827      * @param {Array} records The records to select
57828      * @param {Boolean} keepExisting (optional) True to keep existing selections
57829      */
57830     selectRecords : function(records, keepExisting){
57831         if(!keepExisting){
57832             this.clearSelections();
57833         }
57834         var ds = this.grid.dataSource;
57835         for(var i = 0, len = records.length; i < len; i++){
57836             this.selectRow(ds.indexOf(records[i]), true);
57837         }
57838     },
57839
57840     /**
57841      * Gets the number of selected rows.
57842      * @return {Number}
57843      */
57844     getCount : function(){
57845         return this.selections.length;
57846     },
57847
57848     /**
57849      * Selects the first row in the grid.
57850      */
57851     selectFirstRow : function(){
57852         this.selectRow(0);
57853     },
57854
57855     /**
57856      * Select the last row.
57857      * @param {Boolean} keepExisting (optional) True to keep existing selections
57858      */
57859     selectLastRow : function(keepExisting){
57860         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
57861     },
57862
57863     /**
57864      * Selects the row immediately following the last selected row.
57865      * @param {Boolean} keepExisting (optional) True to keep existing selections
57866      */
57867     selectNext : function(keepExisting){
57868         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
57869             this.selectRow(this.last+1, keepExisting);
57870             this.grid.getView().focusRow(this.last);
57871         }
57872     },
57873
57874     /**
57875      * Selects the row that precedes the last selected row.
57876      * @param {Boolean} keepExisting (optional) True to keep existing selections
57877      */
57878     selectPrevious : function(keepExisting){
57879         if(this.last){
57880             this.selectRow(this.last-1, keepExisting);
57881             this.grid.getView().focusRow(this.last);
57882         }
57883     },
57884
57885     /**
57886      * Returns the selected records
57887      * @return {Array} Array of selected records
57888      */
57889     getSelections : function(){
57890         return [].concat(this.selections.items);
57891     },
57892
57893     /**
57894      * Returns the first selected record.
57895      * @return {Record}
57896      */
57897     getSelected : function(){
57898         return this.selections.itemAt(0);
57899     },
57900
57901
57902     /**
57903      * Clears all selections.
57904      */
57905     clearSelections : function(fast){
57906         if(this.locked) {
57907             return;
57908         }
57909         if(fast !== true){
57910             var ds = this.grid.dataSource;
57911             var s = this.selections;
57912             s.each(function(r){
57913                 this.deselectRow(ds.indexOfId(r.id));
57914             }, this);
57915             s.clear();
57916         }else{
57917             this.selections.clear();
57918         }
57919         this.last = false;
57920     },
57921
57922
57923     /**
57924      * Selects all rows.
57925      */
57926     selectAll : function(){
57927         if(this.locked) {
57928             return;
57929         }
57930         this.selections.clear();
57931         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
57932             this.selectRow(i, true);
57933         }
57934     },
57935
57936     /**
57937      * Returns True if there is a selection.
57938      * @return {Boolean}
57939      */
57940     hasSelection : function(){
57941         return this.selections.length > 0;
57942     },
57943
57944     /**
57945      * Returns True if the specified row is selected.
57946      * @param {Number/Record} record The record or index of the record to check
57947      * @return {Boolean}
57948      */
57949     isSelected : function(index){
57950         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
57951         return (r && this.selections.key(r.id) ? true : false);
57952     },
57953
57954     /**
57955      * Returns True if the specified record id is selected.
57956      * @param {String} id The id of record to check
57957      * @return {Boolean}
57958      */
57959     isIdSelected : function(id){
57960         return (this.selections.key(id) ? true : false);
57961     },
57962
57963     // private
57964     handleMouseDown : function(e, t){
57965         var view = this.grid.getView(), rowIndex;
57966         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
57967             return;
57968         };
57969         if(e.shiftKey && this.last !== false){
57970             var last = this.last;
57971             this.selectRange(last, rowIndex, e.ctrlKey);
57972             this.last = last; // reset the last
57973             view.focusRow(rowIndex);
57974         }else{
57975             var isSelected = this.isSelected(rowIndex);
57976             if(e.button !== 0 && isSelected){
57977                 view.focusRow(rowIndex);
57978             }else if(e.ctrlKey && isSelected){
57979                 this.deselectRow(rowIndex);
57980             }else if(!isSelected){
57981                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
57982                 view.focusRow(rowIndex);
57983             }
57984         }
57985         this.fireEvent("afterselectionchange", this);
57986     },
57987     // private
57988     handleDragableRowClick :  function(grid, rowIndex, e) 
57989     {
57990         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
57991             this.selectRow(rowIndex, false);
57992             grid.view.focusRow(rowIndex);
57993              this.fireEvent("afterselectionchange", this);
57994         }
57995     },
57996     
57997     /**
57998      * Selects multiple rows.
57999      * @param {Array} rows Array of the indexes of the row to select
58000      * @param {Boolean} keepExisting (optional) True to keep existing selections
58001      */
58002     selectRows : function(rows, keepExisting){
58003         if(!keepExisting){
58004             this.clearSelections();
58005         }
58006         for(var i = 0, len = rows.length; i < len; i++){
58007             this.selectRow(rows[i], true);
58008         }
58009     },
58010
58011     /**
58012      * Selects a range of rows. All rows in between startRow and endRow are also selected.
58013      * @param {Number} startRow The index of the first row in the range
58014      * @param {Number} endRow The index of the last row in the range
58015      * @param {Boolean} keepExisting (optional) True to retain existing selections
58016      */
58017     selectRange : function(startRow, endRow, keepExisting){
58018         if(this.locked) {
58019             return;
58020         }
58021         if(!keepExisting){
58022             this.clearSelections();
58023         }
58024         if(startRow <= endRow){
58025             for(var i = startRow; i <= endRow; i++){
58026                 this.selectRow(i, true);
58027             }
58028         }else{
58029             for(var i = startRow; i >= endRow; i--){
58030                 this.selectRow(i, true);
58031             }
58032         }
58033     },
58034
58035     /**
58036      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
58037      * @param {Number} startRow The index of the first row in the range
58038      * @param {Number} endRow The index of the last row in the range
58039      */
58040     deselectRange : function(startRow, endRow, preventViewNotify){
58041         if(this.locked) {
58042             return;
58043         }
58044         for(var i = startRow; i <= endRow; i++){
58045             this.deselectRow(i, preventViewNotify);
58046         }
58047     },
58048
58049     /**
58050      * Selects a row.
58051      * @param {Number} row The index of the row to select
58052      * @param {Boolean} keepExisting (optional) True to keep existing selections
58053      */
58054     selectRow : function(index, keepExisting, preventViewNotify){
58055         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
58056             return;
58057         }
58058         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
58059             if(!keepExisting || this.singleSelect){
58060                 this.clearSelections();
58061             }
58062             var r = this.grid.dataSource.getAt(index);
58063             this.selections.add(r);
58064             this.last = this.lastActive = index;
58065             if(!preventViewNotify){
58066                 this.grid.getView().onRowSelect(index);
58067             }
58068             this.fireEvent("rowselect", this, index, r);
58069             this.fireEvent("selectionchange", this);
58070         }
58071     },
58072
58073     /**
58074      * Deselects a row.
58075      * @param {Number} row The index of the row to deselect
58076      */
58077     deselectRow : function(index, preventViewNotify){
58078         if(this.locked) {
58079             return;
58080         }
58081         if(this.last == index){
58082             this.last = false;
58083         }
58084         if(this.lastActive == index){
58085             this.lastActive = false;
58086         }
58087         var r = this.grid.dataSource.getAt(index);
58088         this.selections.remove(r);
58089         if(!preventViewNotify){
58090             this.grid.getView().onRowDeselect(index);
58091         }
58092         this.fireEvent("rowdeselect", this, index);
58093         this.fireEvent("selectionchange", this);
58094     },
58095
58096     // private
58097     restoreLast : function(){
58098         if(this._last){
58099             this.last = this._last;
58100         }
58101     },
58102
58103     // private
58104     acceptsNav : function(row, col, cm){
58105         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58106     },
58107
58108     // private
58109     onEditorKey : function(field, e){
58110         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
58111         if(k == e.TAB){
58112             e.stopEvent();
58113             ed.completeEdit();
58114             if(e.shiftKey){
58115                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58116             }else{
58117                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58118             }
58119         }else if(k == e.ENTER && !e.ctrlKey){
58120             e.stopEvent();
58121             ed.completeEdit();
58122             if(e.shiftKey){
58123                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
58124             }else{
58125                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
58126             }
58127         }else if(k == e.ESC){
58128             ed.cancelEdit();
58129         }
58130         if(newCell){
58131             g.startEditing(newCell[0], newCell[1]);
58132         }
58133     }
58134 });/*
58135  * Based on:
58136  * Ext JS Library 1.1.1
58137  * Copyright(c) 2006-2007, Ext JS, LLC.
58138  *
58139  * Originally Released Under LGPL - original licence link has changed is not relivant.
58140  *
58141  * Fork - LGPL
58142  * <script type="text/javascript">
58143  */
58144 /**
58145  * @class Roo.grid.CellSelectionModel
58146  * @extends Roo.grid.AbstractSelectionModel
58147  * This class provides the basic implementation for cell selection in a grid.
58148  * @constructor
58149  * @param {Object} config The object containing the configuration of this model.
58150  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
58151  */
58152 Roo.grid.CellSelectionModel = function(config){
58153     Roo.apply(this, config);
58154
58155     this.selection = null;
58156
58157     this.addEvents({
58158         /**
58159              * @event beforerowselect
58160              * Fires before a cell is selected.
58161              * @param {SelectionModel} this
58162              * @param {Number} rowIndex The selected row index
58163              * @param {Number} colIndex The selected cell index
58164              */
58165             "beforecellselect" : true,
58166         /**
58167              * @event cellselect
58168              * Fires when a cell is selected.
58169              * @param {SelectionModel} this
58170              * @param {Number} rowIndex The selected row index
58171              * @param {Number} colIndex The selected cell index
58172              */
58173             "cellselect" : true,
58174         /**
58175              * @event selectionchange
58176              * Fires when the active selection changes.
58177              * @param {SelectionModel} this
58178              * @param {Object} selection null for no selection or an object (o) with two properties
58179                 <ul>
58180                 <li>o.record: the record object for the row the selection is in</li>
58181                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58182                 </ul>
58183              */
58184             "selectionchange" : true,
58185         /**
58186              * @event tabend
58187              * Fires when the tab (or enter) was pressed on the last editable cell
58188              * You can use this to trigger add new row.
58189              * @param {SelectionModel} this
58190              */
58191             "tabend" : true,
58192          /**
58193              * @event beforeeditnext
58194              * Fires before the next editable sell is made active
58195              * You can use this to skip to another cell or fire the tabend
58196              *    if you set cell to false
58197              * @param {Object} eventdata object : { cell : [ row, col ] } 
58198              */
58199             "beforeeditnext" : true
58200     });
58201     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58202 };
58203
58204 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58205     
58206     enter_is_tab: false,
58207
58208     /** @ignore */
58209     initEvents : function(){
58210         this.grid.on("mousedown", this.handleMouseDown, this);
58211         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58212         var view = this.grid.view;
58213         view.on("refresh", this.onViewChange, this);
58214         view.on("rowupdated", this.onRowUpdated, this);
58215         view.on("beforerowremoved", this.clearSelections, this);
58216         view.on("beforerowsinserted", this.clearSelections, this);
58217         if(this.grid.isEditor){
58218             this.grid.on("beforeedit", this.beforeEdit,  this);
58219         }
58220     },
58221
58222         //private
58223     beforeEdit : function(e){
58224         this.select(e.row, e.column, false, true, e.record);
58225     },
58226
58227         //private
58228     onRowUpdated : function(v, index, r){
58229         if(this.selection && this.selection.record == r){
58230             v.onCellSelect(index, this.selection.cell[1]);
58231         }
58232     },
58233
58234         //private
58235     onViewChange : function(){
58236         this.clearSelections(true);
58237     },
58238
58239         /**
58240          * Returns the currently selected cell,.
58241          * @return {Array} The selected cell (row, column) or null if none selected.
58242          */
58243     getSelectedCell : function(){
58244         return this.selection ? this.selection.cell : null;
58245     },
58246
58247     /**
58248      * Clears all selections.
58249      * @param {Boolean} true to prevent the gridview from being notified about the change.
58250      */
58251     clearSelections : function(preventNotify){
58252         var s = this.selection;
58253         if(s){
58254             if(preventNotify !== true){
58255                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58256             }
58257             this.selection = null;
58258             this.fireEvent("selectionchange", this, null);
58259         }
58260     },
58261
58262     /**
58263      * Returns true if there is a selection.
58264      * @return {Boolean}
58265      */
58266     hasSelection : function(){
58267         return this.selection ? true : false;
58268     },
58269
58270     /** @ignore */
58271     handleMouseDown : function(e, t){
58272         var v = this.grid.getView();
58273         if(this.isLocked()){
58274             return;
58275         };
58276         var row = v.findRowIndex(t);
58277         var cell = v.findCellIndex(t);
58278         if(row !== false && cell !== false){
58279             this.select(row, cell);
58280         }
58281     },
58282
58283     /**
58284      * Selects a cell.
58285      * @param {Number} rowIndex
58286      * @param {Number} collIndex
58287      */
58288     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58289         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58290             this.clearSelections();
58291             r = r || this.grid.dataSource.getAt(rowIndex);
58292             this.selection = {
58293                 record : r,
58294                 cell : [rowIndex, colIndex]
58295             };
58296             if(!preventViewNotify){
58297                 var v = this.grid.getView();
58298                 v.onCellSelect(rowIndex, colIndex);
58299                 if(preventFocus !== true){
58300                     v.focusCell(rowIndex, colIndex);
58301                 }
58302             }
58303             this.fireEvent("cellselect", this, rowIndex, colIndex);
58304             this.fireEvent("selectionchange", this, this.selection);
58305         }
58306     },
58307
58308         //private
58309     isSelectable : function(rowIndex, colIndex, cm){
58310         return !cm.isHidden(colIndex);
58311     },
58312
58313     /** @ignore */
58314     handleKeyDown : function(e){
58315         //Roo.log('Cell Sel Model handleKeyDown');
58316         if(!e.isNavKeyPress()){
58317             return;
58318         }
58319         var g = this.grid, s = this.selection;
58320         if(!s){
58321             e.stopEvent();
58322             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58323             if(cell){
58324                 this.select(cell[0], cell[1]);
58325             }
58326             return;
58327         }
58328         var sm = this;
58329         var walk = function(row, col, step){
58330             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58331         };
58332         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58333         var newCell;
58334
58335       
58336
58337         switch(k){
58338             case e.TAB:
58339                 // handled by onEditorKey
58340                 if (g.isEditor && g.editing) {
58341                     return;
58342                 }
58343                 if(e.shiftKey) {
58344                     newCell = walk(r, c-1, -1);
58345                 } else {
58346                     newCell = walk(r, c+1, 1);
58347                 }
58348                 break;
58349             
58350             case e.DOWN:
58351                newCell = walk(r+1, c, 1);
58352                 break;
58353             
58354             case e.UP:
58355                 newCell = walk(r-1, c, -1);
58356                 break;
58357             
58358             case e.RIGHT:
58359                 newCell = walk(r, c+1, 1);
58360                 break;
58361             
58362             case e.LEFT:
58363                 newCell = walk(r, c-1, -1);
58364                 break;
58365             
58366             case e.ENTER:
58367                 
58368                 if(g.isEditor && !g.editing){
58369                    g.startEditing(r, c);
58370                    e.stopEvent();
58371                    return;
58372                 }
58373                 
58374                 
58375              break;
58376         };
58377         if(newCell){
58378             this.select(newCell[0], newCell[1]);
58379             e.stopEvent();
58380             
58381         }
58382     },
58383
58384     acceptsNav : function(row, col, cm){
58385         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58386     },
58387     /**
58388      * Selects a cell.
58389      * @param {Number} field (not used) - as it's normally used as a listener
58390      * @param {Number} e - event - fake it by using
58391      *
58392      * var e = Roo.EventObjectImpl.prototype;
58393      * e.keyCode = e.TAB
58394      *
58395      * 
58396      */
58397     onEditorKey : function(field, e){
58398         
58399         var k = e.getKey(),
58400             newCell,
58401             g = this.grid,
58402             ed = g.activeEditor,
58403             forward = false;
58404         ///Roo.log('onEditorKey' + k);
58405         
58406         
58407         if (this.enter_is_tab && k == e.ENTER) {
58408             k = e.TAB;
58409         }
58410         
58411         if(k == e.TAB){
58412             if(e.shiftKey){
58413                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58414             }else{
58415                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58416                 forward = true;
58417             }
58418             
58419             e.stopEvent();
58420             
58421         } else if(k == e.ENTER &&  !e.ctrlKey){
58422             ed.completeEdit();
58423             e.stopEvent();
58424             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58425         
58426                 } else if(k == e.ESC){
58427             ed.cancelEdit();
58428         }
58429                 
58430         if (newCell) {
58431             var ecall = { cell : newCell, forward : forward };
58432             this.fireEvent('beforeeditnext', ecall );
58433             newCell = ecall.cell;
58434                         forward = ecall.forward;
58435         }
58436                 
58437         if(newCell){
58438             //Roo.log('next cell after edit');
58439             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58440         } else if (forward) {
58441             // tabbed past last
58442             this.fireEvent.defer(100, this, ['tabend',this]);
58443         }
58444     }
58445 });/*
58446  * Based on:
58447  * Ext JS Library 1.1.1
58448  * Copyright(c) 2006-2007, Ext JS, LLC.
58449  *
58450  * Originally Released Under LGPL - original licence link has changed is not relivant.
58451  *
58452  * Fork - LGPL
58453  * <script type="text/javascript">
58454  */
58455  
58456 /**
58457  * @class Roo.grid.EditorGrid
58458  * @extends Roo.grid.Grid
58459  * Class for creating and editable grid.
58460  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58461  * The container MUST have some type of size defined for the grid to fill. The container will be 
58462  * automatically set to position relative if it isn't already.
58463  * @param {Object} dataSource The data model to bind to
58464  * @param {Object} colModel The column model with info about this grid's columns
58465  */
58466 Roo.grid.EditorGrid = function(container, config){
58467     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58468     this.getGridEl().addClass("xedit-grid");
58469
58470     if(!this.selModel){
58471         this.selModel = new Roo.grid.CellSelectionModel();
58472     }
58473
58474     this.activeEditor = null;
58475
58476         this.addEvents({
58477             /**
58478              * @event beforeedit
58479              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58480              * <ul style="padding:5px;padding-left:16px;">
58481              * <li>grid - This grid</li>
58482              * <li>record - The record being edited</li>
58483              * <li>field - The field name being edited</li>
58484              * <li>value - The value for the field being edited.</li>
58485              * <li>row - The grid row index</li>
58486              * <li>column - The grid column index</li>
58487              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58488              * </ul>
58489              * @param {Object} e An edit event (see above for description)
58490              */
58491             "beforeedit" : true,
58492             /**
58493              * @event afteredit
58494              * Fires after a cell is edited. <br />
58495              * <ul style="padding:5px;padding-left:16px;">
58496              * <li>grid - This grid</li>
58497              * <li>record - The record being edited</li>
58498              * <li>field - The field name being edited</li>
58499              * <li>value - The value being set</li>
58500              * <li>originalValue - The original value for the field, before the edit.</li>
58501              * <li>row - The grid row index</li>
58502              * <li>column - The grid column index</li>
58503              * </ul>
58504              * @param {Object} e An edit event (see above for description)
58505              */
58506             "afteredit" : true,
58507             /**
58508              * @event validateedit
58509              * Fires after a cell is edited, but before the value is set in the record. 
58510          * You can use this to modify the value being set in the field, Return false
58511              * to cancel the change. The edit event object has the following properties <br />
58512              * <ul style="padding:5px;padding-left:16px;">
58513          * <li>editor - This editor</li>
58514              * <li>grid - This grid</li>
58515              * <li>record - The record being edited</li>
58516              * <li>field - The field name being edited</li>
58517              * <li>value - The value being set</li>
58518              * <li>originalValue - The original value for the field, before the edit.</li>
58519              * <li>row - The grid row index</li>
58520              * <li>column - The grid column index</li>
58521              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58522              * </ul>
58523              * @param {Object} e An edit event (see above for description)
58524              */
58525             "validateedit" : true
58526         });
58527     this.on("bodyscroll", this.stopEditing,  this);
58528     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58529 };
58530
58531 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58532     /**
58533      * @cfg {Number} clicksToEdit
58534      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58535      */
58536     clicksToEdit: 2,
58537
58538     // private
58539     isEditor : true,
58540     // private
58541     trackMouseOver: false, // causes very odd FF errors
58542
58543     onCellDblClick : function(g, row, col){
58544         this.startEditing(row, col);
58545     },
58546
58547     onEditComplete : function(ed, value, startValue){
58548         this.editing = false;
58549         this.activeEditor = null;
58550         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58551         var r = ed.record;
58552         var field = this.colModel.getDataIndex(ed.col);
58553         var e = {
58554             grid: this,
58555             record: r,
58556             field: field,
58557             originalValue: startValue,
58558             value: value,
58559             row: ed.row,
58560             column: ed.col,
58561             cancel:false,
58562             editor: ed
58563         };
58564         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58565         cell.show();
58566           
58567         if(String(value) !== String(startValue)){
58568             
58569             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58570                 r.set(field, e.value);
58571                 // if we are dealing with a combo box..
58572                 // then we also set the 'name' colum to be the displayField
58573                 if (ed.field.displayField && ed.field.name) {
58574                     r.set(ed.field.name, ed.field.el.dom.value);
58575                 }
58576                 
58577                 delete e.cancel; //?? why!!!
58578                 this.fireEvent("afteredit", e);
58579             }
58580         } else {
58581             this.fireEvent("afteredit", e); // always fire it!
58582         }
58583         this.view.focusCell(ed.row, ed.col);
58584     },
58585
58586     /**
58587      * Starts editing the specified for the specified row/column
58588      * @param {Number} rowIndex
58589      * @param {Number} colIndex
58590      */
58591     startEditing : function(row, col){
58592         this.stopEditing();
58593         if(this.colModel.isCellEditable(col, row)){
58594             this.view.ensureVisible(row, col, true);
58595           
58596             var r = this.dataSource.getAt(row);
58597             var field = this.colModel.getDataIndex(col);
58598             var cell = Roo.get(this.view.getCell(row,col));
58599             var e = {
58600                 grid: this,
58601                 record: r,
58602                 field: field,
58603                 value: r.data[field],
58604                 row: row,
58605                 column: col,
58606                 cancel:false 
58607             };
58608             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58609                 this.editing = true;
58610                 var ed = this.colModel.getCellEditor(col, row);
58611                 
58612                 if (!ed) {
58613                     return;
58614                 }
58615                 if(!ed.rendered){
58616                     ed.render(ed.parentEl || document.body);
58617                 }
58618                 ed.field.reset();
58619                
58620                 cell.hide();
58621                 
58622                 (function(){ // complex but required for focus issues in safari, ie and opera
58623                     ed.row = row;
58624                     ed.col = col;
58625                     ed.record = r;
58626                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58627                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58628                     this.activeEditor = ed;
58629                     var v = r.data[field];
58630                     ed.startEdit(this.view.getCell(row, col), v);
58631                     // combo's with 'displayField and name set
58632                     if (ed.field.displayField && ed.field.name) {
58633                         ed.field.el.dom.value = r.data[ed.field.name];
58634                     }
58635                     
58636                     
58637                 }).defer(50, this);
58638             }
58639         }
58640     },
58641         
58642     /**
58643      * Stops any active editing
58644      */
58645     stopEditing : function(){
58646         if(this.activeEditor){
58647             this.activeEditor.completeEdit();
58648         }
58649         this.activeEditor = null;
58650     },
58651         
58652          /**
58653      * Called to get grid's drag proxy text, by default returns this.ddText.
58654      * @return {String}
58655      */
58656     getDragDropText : function(){
58657         var count = this.selModel.getSelectedCell() ? 1 : 0;
58658         return String.format(this.ddText, count, count == 1 ? '' : 's');
58659     }
58660         
58661 });/*
58662  * Based on:
58663  * Ext JS Library 1.1.1
58664  * Copyright(c) 2006-2007, Ext JS, LLC.
58665  *
58666  * Originally Released Under LGPL - original licence link has changed is not relivant.
58667  *
58668  * Fork - LGPL
58669  * <script type="text/javascript">
58670  */
58671
58672 // private - not really -- you end up using it !
58673 // This is a support class used internally by the Grid components
58674
58675 /**
58676  * @class Roo.grid.GridEditor
58677  * @extends Roo.Editor
58678  * Class for creating and editable grid elements.
58679  * @param {Object} config any settings (must include field)
58680  */
58681 Roo.grid.GridEditor = function(field, config){
58682     if (!config && field.field) {
58683         config = field;
58684         field = Roo.factory(config.field, Roo.form);
58685     }
58686     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
58687     field.monitorTab = false;
58688 };
58689
58690 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
58691     
58692     /**
58693      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
58694      */
58695     
58696     alignment: "tl-tl",
58697     autoSize: "width",
58698     hideEl : false,
58699     cls: "x-small-editor x-grid-editor",
58700     shim:false,
58701     shadow:"frame"
58702 });/*
58703  * Based on:
58704  * Ext JS Library 1.1.1
58705  * Copyright(c) 2006-2007, Ext JS, LLC.
58706  *
58707  * Originally Released Under LGPL - original licence link has changed is not relivant.
58708  *
58709  * Fork - LGPL
58710  * <script type="text/javascript">
58711  */
58712   
58713
58714   
58715 Roo.grid.PropertyRecord = Roo.data.Record.create([
58716     {name:'name',type:'string'},  'value'
58717 ]);
58718
58719
58720 Roo.grid.PropertyStore = function(grid, source){
58721     this.grid = grid;
58722     this.store = new Roo.data.Store({
58723         recordType : Roo.grid.PropertyRecord
58724     });
58725     this.store.on('update', this.onUpdate,  this);
58726     if(source){
58727         this.setSource(source);
58728     }
58729     Roo.grid.PropertyStore.superclass.constructor.call(this);
58730 };
58731
58732
58733
58734 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
58735     setSource : function(o){
58736         this.source = o;
58737         this.store.removeAll();
58738         var data = [];
58739         for(var k in o){
58740             if(this.isEditableValue(o[k])){
58741                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
58742             }
58743         }
58744         this.store.loadRecords({records: data}, {}, true);
58745     },
58746
58747     onUpdate : function(ds, record, type){
58748         if(type == Roo.data.Record.EDIT){
58749             var v = record.data['value'];
58750             var oldValue = record.modified['value'];
58751             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
58752                 this.source[record.id] = v;
58753                 record.commit();
58754                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
58755             }else{
58756                 record.reject();
58757             }
58758         }
58759     },
58760
58761     getProperty : function(row){
58762        return this.store.getAt(row);
58763     },
58764
58765     isEditableValue: function(val){
58766         if(val && val instanceof Date){
58767             return true;
58768         }else if(typeof val == 'object' || typeof val == 'function'){
58769             return false;
58770         }
58771         return true;
58772     },
58773
58774     setValue : function(prop, value){
58775         this.source[prop] = value;
58776         this.store.getById(prop).set('value', value);
58777     },
58778
58779     getSource : function(){
58780         return this.source;
58781     }
58782 });
58783
58784 Roo.grid.PropertyColumnModel = function(grid, store){
58785     this.grid = grid;
58786     var g = Roo.grid;
58787     g.PropertyColumnModel.superclass.constructor.call(this, [
58788         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
58789         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
58790     ]);
58791     this.store = store;
58792     this.bselect = Roo.DomHelper.append(document.body, {
58793         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
58794             {tag: 'option', value: 'true', html: 'true'},
58795             {tag: 'option', value: 'false', html: 'false'}
58796         ]
58797     });
58798     Roo.id(this.bselect);
58799     var f = Roo.form;
58800     this.editors = {
58801         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
58802         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
58803         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
58804         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
58805         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
58806     };
58807     this.renderCellDelegate = this.renderCell.createDelegate(this);
58808     this.renderPropDelegate = this.renderProp.createDelegate(this);
58809 };
58810
58811 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
58812     
58813     
58814     nameText : 'Name',
58815     valueText : 'Value',
58816     
58817     dateFormat : 'm/j/Y',
58818     
58819     
58820     renderDate : function(dateVal){
58821         return dateVal.dateFormat(this.dateFormat);
58822     },
58823
58824     renderBool : function(bVal){
58825         return bVal ? 'true' : 'false';
58826     },
58827
58828     isCellEditable : function(colIndex, rowIndex){
58829         return colIndex == 1;
58830     },
58831
58832     getRenderer : function(col){
58833         return col == 1 ?
58834             this.renderCellDelegate : this.renderPropDelegate;
58835     },
58836
58837     renderProp : function(v){
58838         return this.getPropertyName(v);
58839     },
58840
58841     renderCell : function(val){
58842         var rv = val;
58843         if(val instanceof Date){
58844             rv = this.renderDate(val);
58845         }else if(typeof val == 'boolean'){
58846             rv = this.renderBool(val);
58847         }
58848         return Roo.util.Format.htmlEncode(rv);
58849     },
58850
58851     getPropertyName : function(name){
58852         var pn = this.grid.propertyNames;
58853         return pn && pn[name] ? pn[name] : name;
58854     },
58855
58856     getCellEditor : function(colIndex, rowIndex){
58857         var p = this.store.getProperty(rowIndex);
58858         var n = p.data['name'], val = p.data['value'];
58859         
58860         if(typeof(this.grid.customEditors[n]) == 'string'){
58861             return this.editors[this.grid.customEditors[n]];
58862         }
58863         if(typeof(this.grid.customEditors[n]) != 'undefined'){
58864             return this.grid.customEditors[n];
58865         }
58866         if(val instanceof Date){
58867             return this.editors['date'];
58868         }else if(typeof val == 'number'){
58869             return this.editors['number'];
58870         }else if(typeof val == 'boolean'){
58871             return this.editors['boolean'];
58872         }else{
58873             return this.editors['string'];
58874         }
58875     }
58876 });
58877
58878 /**
58879  * @class Roo.grid.PropertyGrid
58880  * @extends Roo.grid.EditorGrid
58881  * This class represents the  interface of a component based property grid control.
58882  * <br><br>Usage:<pre><code>
58883  var grid = new Roo.grid.PropertyGrid("my-container-id", {
58884       
58885  });
58886  // set any options
58887  grid.render();
58888  * </code></pre>
58889   
58890  * @constructor
58891  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58892  * The container MUST have some type of size defined for the grid to fill. The container will be
58893  * automatically set to position relative if it isn't already.
58894  * @param {Object} config A config object that sets properties on this grid.
58895  */
58896 Roo.grid.PropertyGrid = function(container, config){
58897     config = config || {};
58898     var store = new Roo.grid.PropertyStore(this);
58899     this.store = store;
58900     var cm = new Roo.grid.PropertyColumnModel(this, store);
58901     store.store.sort('name', 'ASC');
58902     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
58903         ds: store.store,
58904         cm: cm,
58905         enableColLock:false,
58906         enableColumnMove:false,
58907         stripeRows:false,
58908         trackMouseOver: false,
58909         clicksToEdit:1
58910     }, config));
58911     this.getGridEl().addClass('x-props-grid');
58912     this.lastEditRow = null;
58913     this.on('columnresize', this.onColumnResize, this);
58914     this.addEvents({
58915          /**
58916              * @event beforepropertychange
58917              * Fires before a property changes (return false to stop?)
58918              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58919              * @param {String} id Record Id
58920              * @param {String} newval New Value
58921          * @param {String} oldval Old Value
58922              */
58923         "beforepropertychange": true,
58924         /**
58925              * @event propertychange
58926              * Fires after a property changes
58927              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58928              * @param {String} id Record Id
58929              * @param {String} newval New Value
58930          * @param {String} oldval Old Value
58931              */
58932         "propertychange": true
58933     });
58934     this.customEditors = this.customEditors || {};
58935 };
58936 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
58937     
58938      /**
58939      * @cfg {Object} customEditors map of colnames=> custom editors.
58940      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
58941      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
58942      * false disables editing of the field.
58943          */
58944     
58945       /**
58946      * @cfg {Object} propertyNames map of property Names to their displayed value
58947          */
58948     
58949     render : function(){
58950         Roo.grid.PropertyGrid.superclass.render.call(this);
58951         this.autoSize.defer(100, this);
58952     },
58953
58954     autoSize : function(){
58955         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
58956         if(this.view){
58957             this.view.fitColumns();
58958         }
58959     },
58960
58961     onColumnResize : function(){
58962         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
58963         this.autoSize();
58964     },
58965     /**
58966      * Sets the data for the Grid
58967      * accepts a Key => Value object of all the elements avaiable.
58968      * @param {Object} data  to appear in grid.
58969      */
58970     setSource : function(source){
58971         this.store.setSource(source);
58972         //this.autoSize();
58973     },
58974     /**
58975      * Gets all the data from the grid.
58976      * @return {Object} data  data stored in grid
58977      */
58978     getSource : function(){
58979         return this.store.getSource();
58980     }
58981 });/*
58982   
58983  * Licence LGPL
58984  
58985  */
58986  
58987 /**
58988  * @class Roo.grid.Calendar
58989  * @extends Roo.util.Grid
58990  * This class extends the Grid to provide a calendar widget
58991  * <br><br>Usage:<pre><code>
58992  var grid = new Roo.grid.Calendar("my-container-id", {
58993      ds: myDataStore,
58994      cm: myColModel,
58995      selModel: mySelectionModel,
58996      autoSizeColumns: true,
58997      monitorWindowResize: false,
58998      trackMouseOver: true
58999      eventstore : real data store..
59000  });
59001  // set any options
59002  grid.render();
59003   
59004   * @constructor
59005  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59006  * The container MUST have some type of size defined for the grid to fill. The container will be
59007  * automatically set to position relative if it isn't already.
59008  * @param {Object} config A config object that sets properties on this grid.
59009  */
59010 Roo.grid.Calendar = function(container, config){
59011         // initialize the container
59012         this.container = Roo.get(container);
59013         this.container.update("");
59014         this.container.setStyle("overflow", "hidden");
59015     this.container.addClass('x-grid-container');
59016
59017     this.id = this.container.id;
59018
59019     Roo.apply(this, config);
59020     // check and correct shorthanded configs
59021     
59022     var rows = [];
59023     var d =1;
59024     for (var r = 0;r < 6;r++) {
59025         
59026         rows[r]=[];
59027         for (var c =0;c < 7;c++) {
59028             rows[r][c]= '';
59029         }
59030     }
59031     if (this.eventStore) {
59032         this.eventStore= Roo.factory(this.eventStore, Roo.data);
59033         this.eventStore.on('load',this.onLoad, this);
59034         this.eventStore.on('beforeload',this.clearEvents, this);
59035          
59036     }
59037     
59038     this.dataSource = new Roo.data.Store({
59039             proxy: new Roo.data.MemoryProxy(rows),
59040             reader: new Roo.data.ArrayReader({}, [
59041                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
59042     });
59043
59044     this.dataSource.load();
59045     this.ds = this.dataSource;
59046     this.ds.xmodule = this.xmodule || false;
59047     
59048     
59049     var cellRender = function(v,x,r)
59050     {
59051         return String.format(
59052             '<div class="fc-day  fc-widget-content"><div>' +
59053                 '<div class="fc-event-container"></div>' +
59054                 '<div class="fc-day-number">{0}</div>'+
59055                 
59056                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
59057             '</div></div>', v);
59058     
59059     }
59060     
59061     
59062     this.colModel = new Roo.grid.ColumnModel( [
59063         {
59064             xtype: 'ColumnModel',
59065             xns: Roo.grid,
59066             dataIndex : 'weekday0',
59067             header : 'Sunday',
59068             renderer : cellRender
59069         },
59070         {
59071             xtype: 'ColumnModel',
59072             xns: Roo.grid,
59073             dataIndex : 'weekday1',
59074             header : 'Monday',
59075             renderer : cellRender
59076         },
59077         {
59078             xtype: 'ColumnModel',
59079             xns: Roo.grid,
59080             dataIndex : 'weekday2',
59081             header : 'Tuesday',
59082             renderer : cellRender
59083         },
59084         {
59085             xtype: 'ColumnModel',
59086             xns: Roo.grid,
59087             dataIndex : 'weekday3',
59088             header : 'Wednesday',
59089             renderer : cellRender
59090         },
59091         {
59092             xtype: 'ColumnModel',
59093             xns: Roo.grid,
59094             dataIndex : 'weekday4',
59095             header : 'Thursday',
59096             renderer : cellRender
59097         },
59098         {
59099             xtype: 'ColumnModel',
59100             xns: Roo.grid,
59101             dataIndex : 'weekday5',
59102             header : 'Friday',
59103             renderer : cellRender
59104         },
59105         {
59106             xtype: 'ColumnModel',
59107             xns: Roo.grid,
59108             dataIndex : 'weekday6',
59109             header : 'Saturday',
59110             renderer : cellRender
59111         }
59112     ]);
59113     this.cm = this.colModel;
59114     this.cm.xmodule = this.xmodule || false;
59115  
59116         
59117           
59118     //this.selModel = new Roo.grid.CellSelectionModel();
59119     //this.sm = this.selModel;
59120     //this.selModel.init(this);
59121     
59122     
59123     if(this.width){
59124         this.container.setWidth(this.width);
59125     }
59126
59127     if(this.height){
59128         this.container.setHeight(this.height);
59129     }
59130     /** @private */
59131         this.addEvents({
59132         // raw events
59133         /**
59134          * @event click
59135          * The raw click event for the entire grid.
59136          * @param {Roo.EventObject} e
59137          */
59138         "click" : true,
59139         /**
59140          * @event dblclick
59141          * The raw dblclick event for the entire grid.
59142          * @param {Roo.EventObject} e
59143          */
59144         "dblclick" : true,
59145         /**
59146          * @event contextmenu
59147          * The raw contextmenu event for the entire grid.
59148          * @param {Roo.EventObject} e
59149          */
59150         "contextmenu" : true,
59151         /**
59152          * @event mousedown
59153          * The raw mousedown event for the entire grid.
59154          * @param {Roo.EventObject} e
59155          */
59156         "mousedown" : true,
59157         /**
59158          * @event mouseup
59159          * The raw mouseup event for the entire grid.
59160          * @param {Roo.EventObject} e
59161          */
59162         "mouseup" : true,
59163         /**
59164          * @event mouseover
59165          * The raw mouseover event for the entire grid.
59166          * @param {Roo.EventObject} e
59167          */
59168         "mouseover" : true,
59169         /**
59170          * @event mouseout
59171          * The raw mouseout event for the entire grid.
59172          * @param {Roo.EventObject} e
59173          */
59174         "mouseout" : true,
59175         /**
59176          * @event keypress
59177          * The raw keypress event for the entire grid.
59178          * @param {Roo.EventObject} e
59179          */
59180         "keypress" : true,
59181         /**
59182          * @event keydown
59183          * The raw keydown event for the entire grid.
59184          * @param {Roo.EventObject} e
59185          */
59186         "keydown" : true,
59187
59188         // custom events
59189
59190         /**
59191          * @event cellclick
59192          * Fires when a cell is clicked
59193          * @param {Grid} this
59194          * @param {Number} rowIndex
59195          * @param {Number} columnIndex
59196          * @param {Roo.EventObject} e
59197          */
59198         "cellclick" : true,
59199         /**
59200          * @event celldblclick
59201          * Fires when a cell is double clicked
59202          * @param {Grid} this
59203          * @param {Number} rowIndex
59204          * @param {Number} columnIndex
59205          * @param {Roo.EventObject} e
59206          */
59207         "celldblclick" : true,
59208         /**
59209          * @event rowclick
59210          * Fires when a row is clicked
59211          * @param {Grid} this
59212          * @param {Number} rowIndex
59213          * @param {Roo.EventObject} e
59214          */
59215         "rowclick" : true,
59216         /**
59217          * @event rowdblclick
59218          * Fires when a row is double clicked
59219          * @param {Grid} this
59220          * @param {Number} rowIndex
59221          * @param {Roo.EventObject} e
59222          */
59223         "rowdblclick" : true,
59224         /**
59225          * @event headerclick
59226          * Fires when a header is clicked
59227          * @param {Grid} this
59228          * @param {Number} columnIndex
59229          * @param {Roo.EventObject} e
59230          */
59231         "headerclick" : true,
59232         /**
59233          * @event headerdblclick
59234          * Fires when a header cell is double clicked
59235          * @param {Grid} this
59236          * @param {Number} columnIndex
59237          * @param {Roo.EventObject} e
59238          */
59239         "headerdblclick" : true,
59240         /**
59241          * @event rowcontextmenu
59242          * Fires when a row is right clicked
59243          * @param {Grid} this
59244          * @param {Number} rowIndex
59245          * @param {Roo.EventObject} e
59246          */
59247         "rowcontextmenu" : true,
59248         /**
59249          * @event cellcontextmenu
59250          * Fires when a cell is right clicked
59251          * @param {Grid} this
59252          * @param {Number} rowIndex
59253          * @param {Number} cellIndex
59254          * @param {Roo.EventObject} e
59255          */
59256          "cellcontextmenu" : true,
59257         /**
59258          * @event headercontextmenu
59259          * Fires when a header is right clicked
59260          * @param {Grid} this
59261          * @param {Number} columnIndex
59262          * @param {Roo.EventObject} e
59263          */
59264         "headercontextmenu" : true,
59265         /**
59266          * @event bodyscroll
59267          * Fires when the body element is scrolled
59268          * @param {Number} scrollLeft
59269          * @param {Number} scrollTop
59270          */
59271         "bodyscroll" : true,
59272         /**
59273          * @event columnresize
59274          * Fires when the user resizes a column
59275          * @param {Number} columnIndex
59276          * @param {Number} newSize
59277          */
59278         "columnresize" : true,
59279         /**
59280          * @event columnmove
59281          * Fires when the user moves a column
59282          * @param {Number} oldIndex
59283          * @param {Number} newIndex
59284          */
59285         "columnmove" : true,
59286         /**
59287          * @event startdrag
59288          * Fires when row(s) start being dragged
59289          * @param {Grid} this
59290          * @param {Roo.GridDD} dd The drag drop object
59291          * @param {event} e The raw browser event
59292          */
59293         "startdrag" : true,
59294         /**
59295          * @event enddrag
59296          * Fires when a drag operation is complete
59297          * @param {Grid} this
59298          * @param {Roo.GridDD} dd The drag drop object
59299          * @param {event} e The raw browser event
59300          */
59301         "enddrag" : true,
59302         /**
59303          * @event dragdrop
59304          * Fires when dragged row(s) are dropped on a valid DD target
59305          * @param {Grid} this
59306          * @param {Roo.GridDD} dd The drag drop object
59307          * @param {String} targetId The target drag drop object
59308          * @param {event} e The raw browser event
59309          */
59310         "dragdrop" : true,
59311         /**
59312          * @event dragover
59313          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59314          * @param {Grid} this
59315          * @param {Roo.GridDD} dd The drag drop object
59316          * @param {String} targetId The target drag drop object
59317          * @param {event} e The raw browser event
59318          */
59319         "dragover" : true,
59320         /**
59321          * @event dragenter
59322          *  Fires when the dragged row(s) first cross another DD target while being dragged
59323          * @param {Grid} this
59324          * @param {Roo.GridDD} dd The drag drop object
59325          * @param {String} targetId The target drag drop object
59326          * @param {event} e The raw browser event
59327          */
59328         "dragenter" : true,
59329         /**
59330          * @event dragout
59331          * Fires when the dragged row(s) leave another DD target while being dragged
59332          * @param {Grid} this
59333          * @param {Roo.GridDD} dd The drag drop object
59334          * @param {String} targetId The target drag drop object
59335          * @param {event} e The raw browser event
59336          */
59337         "dragout" : true,
59338         /**
59339          * @event rowclass
59340          * Fires when a row is rendered, so you can change add a style to it.
59341          * @param {GridView} gridview   The grid view
59342          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59343          */
59344         'rowclass' : true,
59345
59346         /**
59347          * @event render
59348          * Fires when the grid is rendered
59349          * @param {Grid} grid
59350          */
59351         'render' : true,
59352             /**
59353              * @event select
59354              * Fires when a date is selected
59355              * @param {DatePicker} this
59356              * @param {Date} date The selected date
59357              */
59358         'select': true,
59359         /**
59360              * @event monthchange
59361              * Fires when the displayed month changes 
59362              * @param {DatePicker} this
59363              * @param {Date} date The selected month
59364              */
59365         'monthchange': true,
59366         /**
59367              * @event evententer
59368              * Fires when mouse over an event
59369              * @param {Calendar} this
59370              * @param {event} Event
59371              */
59372         'evententer': true,
59373         /**
59374              * @event eventleave
59375              * Fires when the mouse leaves an
59376              * @param {Calendar} this
59377              * @param {event}
59378              */
59379         'eventleave': true,
59380         /**
59381              * @event eventclick
59382              * Fires when the mouse click an
59383              * @param {Calendar} this
59384              * @param {event}
59385              */
59386         'eventclick': true,
59387         /**
59388              * @event eventrender
59389              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59390              * @param {Calendar} this
59391              * @param {data} data to be modified
59392              */
59393         'eventrender': true
59394         
59395     });
59396
59397     Roo.grid.Grid.superclass.constructor.call(this);
59398     this.on('render', function() {
59399         this.view.el.addClass('x-grid-cal'); 
59400         
59401         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59402
59403     },this);
59404     
59405     if (!Roo.grid.Calendar.style) {
59406         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59407             
59408             
59409             '.x-grid-cal .x-grid-col' :  {
59410                 height: 'auto !important',
59411                 'vertical-align': 'top'
59412             },
59413             '.x-grid-cal  .fc-event-hori' : {
59414                 height: '14px'
59415             }
59416              
59417             
59418         }, Roo.id());
59419     }
59420
59421     
59422     
59423 };
59424 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59425     /**
59426      * @cfg {Store} eventStore The store that loads events.
59427      */
59428     eventStore : 25,
59429
59430      
59431     activeDate : false,
59432     startDay : 0,
59433     autoWidth : true,
59434     monitorWindowResize : false,
59435
59436     
59437     resizeColumns : function() {
59438         var col = (this.view.el.getWidth() / 7) - 3;
59439         // loop through cols, and setWidth
59440         for(var i =0 ; i < 7 ; i++){
59441             this.cm.setColumnWidth(i, col);
59442         }
59443     },
59444      setDate :function(date) {
59445         
59446         Roo.log('setDate?');
59447         
59448         this.resizeColumns();
59449         var vd = this.activeDate;
59450         this.activeDate = date;
59451 //        if(vd && this.el){
59452 //            var t = date.getTime();
59453 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59454 //                Roo.log('using add remove');
59455 //                
59456 //                this.fireEvent('monthchange', this, date);
59457 //                
59458 //                this.cells.removeClass("fc-state-highlight");
59459 //                this.cells.each(function(c){
59460 //                   if(c.dateValue == t){
59461 //                       c.addClass("fc-state-highlight");
59462 //                       setTimeout(function(){
59463 //                            try{c.dom.firstChild.focus();}catch(e){}
59464 //                       }, 50);
59465 //                       return false;
59466 //                   }
59467 //                   return true;
59468 //                });
59469 //                return;
59470 //            }
59471 //        }
59472         
59473         var days = date.getDaysInMonth();
59474         
59475         var firstOfMonth = date.getFirstDateOfMonth();
59476         var startingPos = firstOfMonth.getDay()-this.startDay;
59477         
59478         if(startingPos < this.startDay){
59479             startingPos += 7;
59480         }
59481         
59482         var pm = date.add(Date.MONTH, -1);
59483         var prevStart = pm.getDaysInMonth()-startingPos;
59484 //        
59485         
59486         
59487         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59488         
59489         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59490         //this.cells.addClassOnOver('fc-state-hover');
59491         
59492         var cells = this.cells.elements;
59493         var textEls = this.textNodes;
59494         
59495         //Roo.each(cells, function(cell){
59496         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59497         //});
59498         
59499         days += startingPos;
59500
59501         // convert everything to numbers so it's fast
59502         var day = 86400000;
59503         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59504         //Roo.log(d);
59505         //Roo.log(pm);
59506         //Roo.log(prevStart);
59507         
59508         var today = new Date().clearTime().getTime();
59509         var sel = date.clearTime().getTime();
59510         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59511         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59512         var ddMatch = this.disabledDatesRE;
59513         var ddText = this.disabledDatesText;
59514         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59515         var ddaysText = this.disabledDaysText;
59516         var format = this.format;
59517         
59518         var setCellClass = function(cal, cell){
59519             
59520             //Roo.log('set Cell Class');
59521             cell.title = "";
59522             var t = d.getTime();
59523             
59524             //Roo.log(d);
59525             
59526             
59527             cell.dateValue = t;
59528             if(t == today){
59529                 cell.className += " fc-today";
59530                 cell.className += " fc-state-highlight";
59531                 cell.title = cal.todayText;
59532             }
59533             if(t == sel){
59534                 // disable highlight in other month..
59535                 cell.className += " fc-state-highlight";
59536                 
59537             }
59538             // disabling
59539             if(t < min) {
59540                 //cell.className = " fc-state-disabled";
59541                 cell.title = cal.minText;
59542                 return;
59543             }
59544             if(t > max) {
59545                 //cell.className = " fc-state-disabled";
59546                 cell.title = cal.maxText;
59547                 return;
59548             }
59549             if(ddays){
59550                 if(ddays.indexOf(d.getDay()) != -1){
59551                     // cell.title = ddaysText;
59552                    // cell.className = " fc-state-disabled";
59553                 }
59554             }
59555             if(ddMatch && format){
59556                 var fvalue = d.dateFormat(format);
59557                 if(ddMatch.test(fvalue)){
59558                     cell.title = ddText.replace("%0", fvalue);
59559                    cell.className = " fc-state-disabled";
59560                 }
59561             }
59562             
59563             if (!cell.initialClassName) {
59564                 cell.initialClassName = cell.dom.className;
59565             }
59566             
59567             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59568         };
59569
59570         var i = 0;
59571         
59572         for(; i < startingPos; i++) {
59573             cells[i].dayName =  (++prevStart);
59574             Roo.log(textEls[i]);
59575             d.setDate(d.getDate()+1);
59576             
59577             //cells[i].className = "fc-past fc-other-month";
59578             setCellClass(this, cells[i]);
59579         }
59580         
59581         var intDay = 0;
59582         
59583         for(; i < days; i++){
59584             intDay = i - startingPos + 1;
59585             cells[i].dayName =  (intDay);
59586             d.setDate(d.getDate()+1);
59587             
59588             cells[i].className = ''; // "x-date-active";
59589             setCellClass(this, cells[i]);
59590         }
59591         var extraDays = 0;
59592         
59593         for(; i < 42; i++) {
59594             //textEls[i].innerHTML = (++extraDays);
59595             
59596             d.setDate(d.getDate()+1);
59597             cells[i].dayName = (++extraDays);
59598             cells[i].className = "fc-future fc-other-month";
59599             setCellClass(this, cells[i]);
59600         }
59601         
59602         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59603         
59604         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59605         
59606         // this will cause all the cells to mis
59607         var rows= [];
59608         var i =0;
59609         for (var r = 0;r < 6;r++) {
59610             for (var c =0;c < 7;c++) {
59611                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59612             }    
59613         }
59614         
59615         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59616         for(i=0;i<cells.length;i++) {
59617             
59618             this.cells.elements[i].dayName = cells[i].dayName ;
59619             this.cells.elements[i].className = cells[i].className;
59620             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59621             this.cells.elements[i].title = cells[i].title ;
59622             this.cells.elements[i].dateValue = cells[i].dateValue ;
59623         }
59624         
59625         
59626         
59627         
59628         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59629         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59630         
59631         ////if(totalRows != 6){
59632             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59633            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59634        // }
59635         
59636         this.fireEvent('monthchange', this, date);
59637         
59638         
59639     },
59640  /**
59641      * Returns the grid's SelectionModel.
59642      * @return {SelectionModel}
59643      */
59644     getSelectionModel : function(){
59645         if(!this.selModel){
59646             this.selModel = new Roo.grid.CellSelectionModel();
59647         }
59648         return this.selModel;
59649     },
59650
59651     load: function() {
59652         this.eventStore.load()
59653         
59654         
59655         
59656     },
59657     
59658     findCell : function(dt) {
59659         dt = dt.clearTime().getTime();
59660         var ret = false;
59661         this.cells.each(function(c){
59662             //Roo.log("check " +c.dateValue + '?=' + dt);
59663             if(c.dateValue == dt){
59664                 ret = c;
59665                 return false;
59666             }
59667             return true;
59668         });
59669         
59670         return ret;
59671     },
59672     
59673     findCells : function(rec) {
59674         var s = rec.data.start_dt.clone().clearTime().getTime();
59675        // Roo.log(s);
59676         var e= rec.data.end_dt.clone().clearTime().getTime();
59677        // Roo.log(e);
59678         var ret = [];
59679         this.cells.each(function(c){
59680              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
59681             
59682             if(c.dateValue > e){
59683                 return ;
59684             }
59685             if(c.dateValue < s){
59686                 return ;
59687             }
59688             ret.push(c);
59689         });
59690         
59691         return ret;    
59692     },
59693     
59694     findBestRow: function(cells)
59695     {
59696         var ret = 0;
59697         
59698         for (var i =0 ; i < cells.length;i++) {
59699             ret  = Math.max(cells[i].rows || 0,ret);
59700         }
59701         return ret;
59702         
59703     },
59704     
59705     
59706     addItem : function(rec)
59707     {
59708         // look for vertical location slot in
59709         var cells = this.findCells(rec);
59710         
59711         rec.row = this.findBestRow(cells);
59712         
59713         // work out the location.
59714         
59715         var crow = false;
59716         var rows = [];
59717         for(var i =0; i < cells.length; i++) {
59718             if (!crow) {
59719                 crow = {
59720                     start : cells[i],
59721                     end :  cells[i]
59722                 };
59723                 continue;
59724             }
59725             if (crow.start.getY() == cells[i].getY()) {
59726                 // on same row.
59727                 crow.end = cells[i];
59728                 continue;
59729             }
59730             // different row.
59731             rows.push(crow);
59732             crow = {
59733                 start: cells[i],
59734                 end : cells[i]
59735             };
59736             
59737         }
59738         
59739         rows.push(crow);
59740         rec.els = [];
59741         rec.rows = rows;
59742         rec.cells = cells;
59743         for (var i = 0; i < cells.length;i++) {
59744             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
59745             
59746         }
59747         
59748         
59749     },
59750     
59751     clearEvents: function() {
59752         
59753         if (!this.eventStore.getCount()) {
59754             return;
59755         }
59756         // reset number of rows in cells.
59757         Roo.each(this.cells.elements, function(c){
59758             c.rows = 0;
59759         });
59760         
59761         this.eventStore.each(function(e) {
59762             this.clearEvent(e);
59763         },this);
59764         
59765     },
59766     
59767     clearEvent : function(ev)
59768     {
59769         if (ev.els) {
59770             Roo.each(ev.els, function(el) {
59771                 el.un('mouseenter' ,this.onEventEnter, this);
59772                 el.un('mouseleave' ,this.onEventLeave, this);
59773                 el.remove();
59774             },this);
59775             ev.els = [];
59776         }
59777     },
59778     
59779     
59780     renderEvent : function(ev,ctr) {
59781         if (!ctr) {
59782              ctr = this.view.el.select('.fc-event-container',true).first();
59783         }
59784         
59785          
59786         this.clearEvent(ev);
59787             //code
59788        
59789         
59790         
59791         ev.els = [];
59792         var cells = ev.cells;
59793         var rows = ev.rows;
59794         this.fireEvent('eventrender', this, ev);
59795         
59796         for(var i =0; i < rows.length; i++) {
59797             
59798             cls = '';
59799             if (i == 0) {
59800                 cls += ' fc-event-start';
59801             }
59802             if ((i+1) == rows.length) {
59803                 cls += ' fc-event-end';
59804             }
59805             
59806             //Roo.log(ev.data);
59807             // how many rows should it span..
59808             var cg = this.eventTmpl.append(ctr,Roo.apply({
59809                 fccls : cls
59810                 
59811             }, ev.data) , true);
59812             
59813             
59814             cg.on('mouseenter' ,this.onEventEnter, this, ev);
59815             cg.on('mouseleave' ,this.onEventLeave, this, ev);
59816             cg.on('click', this.onEventClick, this, ev);
59817             
59818             ev.els.push(cg);
59819             
59820             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
59821             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
59822             //Roo.log(cg);
59823              
59824             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
59825             cg.setWidth(ebox.right - sbox.x -2);
59826         }
59827     },
59828     
59829     renderEvents: function()
59830     {   
59831         // first make sure there is enough space..
59832         
59833         if (!this.eventTmpl) {
59834             this.eventTmpl = new Roo.Template(
59835                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
59836                     '<div class="fc-event-inner">' +
59837                         '<span class="fc-event-time">{time}</span>' +
59838                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
59839                     '</div>' +
59840                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
59841                 '</div>'
59842             );
59843                 
59844         }
59845                
59846         
59847         
59848         this.cells.each(function(c) {
59849             //Roo.log(c.select('.fc-day-content div',true).first());
59850             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
59851         });
59852         
59853         var ctr = this.view.el.select('.fc-event-container',true).first();
59854         
59855         var cls;
59856         this.eventStore.each(function(ev){
59857             
59858             this.renderEvent(ev);
59859              
59860              
59861         }, this);
59862         this.view.layout();
59863         
59864     },
59865     
59866     onEventEnter: function (e, el,event,d) {
59867         this.fireEvent('evententer', this, el, event);
59868     },
59869     
59870     onEventLeave: function (e, el,event,d) {
59871         this.fireEvent('eventleave', this, el, event);
59872     },
59873     
59874     onEventClick: function (e, el,event,d) {
59875         this.fireEvent('eventclick', this, el, event);
59876     },
59877     
59878     onMonthChange: function () {
59879         this.store.load();
59880     },
59881     
59882     onLoad: function () {
59883         
59884         //Roo.log('calendar onload');
59885 //         
59886         if(this.eventStore.getCount() > 0){
59887             
59888            
59889             
59890             this.eventStore.each(function(d){
59891                 
59892                 
59893                 // FIXME..
59894                 var add =   d.data;
59895                 if (typeof(add.end_dt) == 'undefined')  {
59896                     Roo.log("Missing End time in calendar data: ");
59897                     Roo.log(d);
59898                     return;
59899                 }
59900                 if (typeof(add.start_dt) == 'undefined')  {
59901                     Roo.log("Missing Start time in calendar data: ");
59902                     Roo.log(d);
59903                     return;
59904                 }
59905                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
59906                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
59907                 add.id = add.id || d.id;
59908                 add.title = add.title || '??';
59909                 
59910                 this.addItem(d);
59911                 
59912              
59913             },this);
59914         }
59915         
59916         this.renderEvents();
59917     }
59918     
59919
59920 });
59921 /*
59922  grid : {
59923                 xtype: 'Grid',
59924                 xns: Roo.grid,
59925                 listeners : {
59926                     render : function ()
59927                     {
59928                         _this.grid = this;
59929                         
59930                         if (!this.view.el.hasClass('course-timesheet')) {
59931                             this.view.el.addClass('course-timesheet');
59932                         }
59933                         if (this.tsStyle) {
59934                             this.ds.load({});
59935                             return; 
59936                         }
59937                         Roo.log('width');
59938                         Roo.log(_this.grid.view.el.getWidth());
59939                         
59940                         
59941                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
59942                             '.course-timesheet .x-grid-row' : {
59943                                 height: '80px'
59944                             },
59945                             '.x-grid-row td' : {
59946                                 'vertical-align' : 0
59947                             },
59948                             '.course-edit-link' : {
59949                                 'color' : 'blue',
59950                                 'text-overflow' : 'ellipsis',
59951                                 'overflow' : 'hidden',
59952                                 'white-space' : 'nowrap',
59953                                 'cursor' : 'pointer'
59954                             },
59955                             '.sub-link' : {
59956                                 'color' : 'green'
59957                             },
59958                             '.de-act-sup-link' : {
59959                                 'color' : 'purple',
59960                                 'text-decoration' : 'line-through'
59961                             },
59962                             '.de-act-link' : {
59963                                 'color' : 'red',
59964                                 'text-decoration' : 'line-through'
59965                             },
59966                             '.course-timesheet .course-highlight' : {
59967                                 'border-top-style': 'dashed !important',
59968                                 'border-bottom-bottom': 'dashed !important'
59969                             },
59970                             '.course-timesheet .course-item' : {
59971                                 'font-family'   : 'tahoma, arial, helvetica',
59972                                 'font-size'     : '11px',
59973                                 'overflow'      : 'hidden',
59974                                 'padding-left'  : '10px',
59975                                 'padding-right' : '10px',
59976                                 'padding-top' : '10px' 
59977                             }
59978                             
59979                         }, Roo.id());
59980                                 this.ds.load({});
59981                     }
59982                 },
59983                 autoWidth : true,
59984                 monitorWindowResize : false,
59985                 cellrenderer : function(v,x,r)
59986                 {
59987                     return v;
59988                 },
59989                 sm : {
59990                     xtype: 'CellSelectionModel',
59991                     xns: Roo.grid
59992                 },
59993                 dataSource : {
59994                     xtype: 'Store',
59995                     xns: Roo.data,
59996                     listeners : {
59997                         beforeload : function (_self, options)
59998                         {
59999                             options.params = options.params || {};
60000                             options.params._month = _this.monthField.getValue();
60001                             options.params.limit = 9999;
60002                             options.params['sort'] = 'when_dt';    
60003                             options.params['dir'] = 'ASC';    
60004                             this.proxy.loadResponse = this.loadResponse;
60005                             Roo.log("load?");
60006                             //this.addColumns();
60007                         },
60008                         load : function (_self, records, options)
60009                         {
60010                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
60011                                 // if you click on the translation.. you can edit it...
60012                                 var el = Roo.get(this);
60013                                 var id = el.dom.getAttribute('data-id');
60014                                 var d = el.dom.getAttribute('data-date');
60015                                 var t = el.dom.getAttribute('data-time');
60016                                 //var id = this.child('span').dom.textContent;
60017                                 
60018                                 //Roo.log(this);
60019                                 Pman.Dialog.CourseCalendar.show({
60020                                     id : id,
60021                                     when_d : d,
60022                                     when_t : t,
60023                                     productitem_active : id ? 1 : 0
60024                                 }, function() {
60025                                     _this.grid.ds.load({});
60026                                 });
60027                            
60028                            });
60029                            
60030                            _this.panel.fireEvent('resize', [ '', '' ]);
60031                         }
60032                     },
60033                     loadResponse : function(o, success, response){
60034                             // this is overridden on before load..
60035                             
60036                             Roo.log("our code?");       
60037                             //Roo.log(success);
60038                             //Roo.log(response)
60039                             delete this.activeRequest;
60040                             if(!success){
60041                                 this.fireEvent("loadexception", this, o, response);
60042                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60043                                 return;
60044                             }
60045                             var result;
60046                             try {
60047                                 result = o.reader.read(response);
60048                             }catch(e){
60049                                 Roo.log("load exception?");
60050                                 this.fireEvent("loadexception", this, o, response, e);
60051                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60052                                 return;
60053                             }
60054                             Roo.log("ready...");        
60055                             // loop through result.records;
60056                             // and set this.tdate[date] = [] << array of records..
60057                             _this.tdata  = {};
60058                             Roo.each(result.records, function(r){
60059                                 //Roo.log(r.data);
60060                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
60061                                     _this.tdata[r.data.when_dt.format('j')] = [];
60062                                 }
60063                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
60064                             });
60065                             
60066                             //Roo.log(_this.tdata);
60067                             
60068                             result.records = [];
60069                             result.totalRecords = 6;
60070                     
60071                             // let's generate some duumy records for the rows.
60072                             //var st = _this.dateField.getValue();
60073                             
60074                             // work out monday..
60075                             //st = st.add(Date.DAY, -1 * st.format('w'));
60076                             
60077                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60078                             
60079                             var firstOfMonth = date.getFirstDayOfMonth();
60080                             var days = date.getDaysInMonth();
60081                             var d = 1;
60082                             var firstAdded = false;
60083                             for (var i = 0; i < result.totalRecords ; i++) {
60084                                 //var d= st.add(Date.DAY, i);
60085                                 var row = {};
60086                                 var added = 0;
60087                                 for(var w = 0 ; w < 7 ; w++){
60088                                     if(!firstAdded && firstOfMonth != w){
60089                                         continue;
60090                                     }
60091                                     if(d > days){
60092                                         continue;
60093                                     }
60094                                     firstAdded = true;
60095                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
60096                                     row['weekday'+w] = String.format(
60097                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
60098                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
60099                                                     d,
60100                                                     date.format('Y-m-')+dd
60101                                                 );
60102                                     added++;
60103                                     if(typeof(_this.tdata[d]) != 'undefined'){
60104                                         Roo.each(_this.tdata[d], function(r){
60105                                             var is_sub = '';
60106                                             var deactive = '';
60107                                             var id = r.id;
60108                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
60109                                             if(r.parent_id*1>0){
60110                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
60111                                                 id = r.parent_id;
60112                                             }
60113                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
60114                                                 deactive = 'de-act-link';
60115                                             }
60116                                             
60117                                             row['weekday'+w] += String.format(
60118                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
60119                                                     id, //0
60120                                                     r.product_id_name, //1
60121                                                     r.when_dt.format('h:ia'), //2
60122                                                     is_sub, //3
60123                                                     deactive, //4
60124                                                     desc // 5
60125                                             );
60126                                         });
60127                                     }
60128                                     d++;
60129                                 }
60130                                 
60131                                 // only do this if something added..
60132                                 if(added > 0){ 
60133                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
60134                                 }
60135                                 
60136                                 
60137                                 // push it twice. (second one with an hour..
60138                                 
60139                             }
60140                             //Roo.log(result);
60141                             this.fireEvent("load", this, o, o.request.arg);
60142                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
60143                         },
60144                     sortInfo : {field: 'when_dt', direction : 'ASC' },
60145                     proxy : {
60146                         xtype: 'HttpProxy',
60147                         xns: Roo.data,
60148                         method : 'GET',
60149                         url : baseURL + '/Roo/Shop_course.php'
60150                     },
60151                     reader : {
60152                         xtype: 'JsonReader',
60153                         xns: Roo.data,
60154                         id : 'id',
60155                         fields : [
60156                             {
60157                                 'name': 'id',
60158                                 'type': 'int'
60159                             },
60160                             {
60161                                 'name': 'when_dt',
60162                                 'type': 'string'
60163                             },
60164                             {
60165                                 'name': 'end_dt',
60166                                 'type': 'string'
60167                             },
60168                             {
60169                                 'name': 'parent_id',
60170                                 'type': 'int'
60171                             },
60172                             {
60173                                 'name': 'product_id',
60174                                 'type': 'int'
60175                             },
60176                             {
60177                                 'name': 'productitem_id',
60178                                 'type': 'int'
60179                             },
60180                             {
60181                                 'name': 'guid',
60182                                 'type': 'int'
60183                             }
60184                         ]
60185                     }
60186                 },
60187                 toolbar : {
60188                     xtype: 'Toolbar',
60189                     xns: Roo,
60190                     items : [
60191                         {
60192                             xtype: 'Button',
60193                             xns: Roo.Toolbar,
60194                             listeners : {
60195                                 click : function (_self, e)
60196                                 {
60197                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60198                                     sd.setMonth(sd.getMonth()-1);
60199                                     _this.monthField.setValue(sd.format('Y-m-d'));
60200                                     _this.grid.ds.load({});
60201                                 }
60202                             },
60203                             text : "Back"
60204                         },
60205                         {
60206                             xtype: 'Separator',
60207                             xns: Roo.Toolbar
60208                         },
60209                         {
60210                             xtype: 'MonthField',
60211                             xns: Roo.form,
60212                             listeners : {
60213                                 render : function (_self)
60214                                 {
60215                                     _this.monthField = _self;
60216                                    // _this.monthField.set  today
60217                                 },
60218                                 select : function (combo, date)
60219                                 {
60220                                     _this.grid.ds.load({});
60221                                 }
60222                             },
60223                             value : (function() { return new Date(); })()
60224                         },
60225                         {
60226                             xtype: 'Separator',
60227                             xns: Roo.Toolbar
60228                         },
60229                         {
60230                             xtype: 'TextItem',
60231                             xns: Roo.Toolbar,
60232                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60233                         },
60234                         {
60235                             xtype: 'Fill',
60236                             xns: Roo.Toolbar
60237                         },
60238                         {
60239                             xtype: 'Button',
60240                             xns: Roo.Toolbar,
60241                             listeners : {
60242                                 click : function (_self, e)
60243                                 {
60244                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60245                                     sd.setMonth(sd.getMonth()+1);
60246                                     _this.monthField.setValue(sd.format('Y-m-d'));
60247                                     _this.grid.ds.load({});
60248                                 }
60249                             },
60250                             text : "Next"
60251                         }
60252                     ]
60253                 },
60254                  
60255             }
60256         };
60257         
60258         *//*
60259  * Based on:
60260  * Ext JS Library 1.1.1
60261  * Copyright(c) 2006-2007, Ext JS, LLC.
60262  *
60263  * Originally Released Under LGPL - original licence link has changed is not relivant.
60264  *
60265  * Fork - LGPL
60266  * <script type="text/javascript">
60267  */
60268  
60269 /**
60270  * @class Roo.LoadMask
60271  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60272  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60273  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60274  * element's UpdateManager load indicator and will be destroyed after the initial load.
60275  * @constructor
60276  * Create a new LoadMask
60277  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60278  * @param {Object} config The config object
60279  */
60280 Roo.LoadMask = function(el, config){
60281     this.el = Roo.get(el);
60282     Roo.apply(this, config);
60283     if(this.store){
60284         this.store.on('beforeload', this.onBeforeLoad, this);
60285         this.store.on('load', this.onLoad, this);
60286         this.store.on('loadexception', this.onLoadException, this);
60287         this.removeMask = false;
60288     }else{
60289         var um = this.el.getUpdateManager();
60290         um.showLoadIndicator = false; // disable the default indicator
60291         um.on('beforeupdate', this.onBeforeLoad, this);
60292         um.on('update', this.onLoad, this);
60293         um.on('failure', this.onLoad, this);
60294         this.removeMask = true;
60295     }
60296 };
60297
60298 Roo.LoadMask.prototype = {
60299     /**
60300      * @cfg {Boolean} removeMask
60301      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60302      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60303      */
60304     /**
60305      * @cfg {String} msg
60306      * The text to display in a centered loading message box (defaults to 'Loading...')
60307      */
60308     msg : 'Loading...',
60309     /**
60310      * @cfg {String} msgCls
60311      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60312      */
60313     msgCls : 'x-mask-loading',
60314
60315     /**
60316      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60317      * @type Boolean
60318      */
60319     disabled: false,
60320
60321     /**
60322      * Disables the mask to prevent it from being displayed
60323      */
60324     disable : function(){
60325        this.disabled = true;
60326     },
60327
60328     /**
60329      * Enables the mask so that it can be displayed
60330      */
60331     enable : function(){
60332         this.disabled = false;
60333     },
60334     
60335     onLoadException : function()
60336     {
60337         Roo.log(arguments);
60338         
60339         if (typeof(arguments[3]) != 'undefined') {
60340             Roo.MessageBox.alert("Error loading",arguments[3]);
60341         } 
60342         /*
60343         try {
60344             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60345                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60346             }   
60347         } catch(e) {
60348             
60349         }
60350         */
60351     
60352         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60353     },
60354     // private
60355     onLoad : function()
60356     {
60357         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60358     },
60359
60360     // private
60361     onBeforeLoad : function(){
60362         if(!this.disabled){
60363             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
60364         }
60365     },
60366
60367     // private
60368     destroy : function(){
60369         if(this.store){
60370             this.store.un('beforeload', this.onBeforeLoad, this);
60371             this.store.un('load', this.onLoad, this);
60372             this.store.un('loadexception', this.onLoadException, this);
60373         }else{
60374             var um = this.el.getUpdateManager();
60375             um.un('beforeupdate', this.onBeforeLoad, this);
60376             um.un('update', this.onLoad, this);
60377             um.un('failure', this.onLoad, this);
60378         }
60379     }
60380 };/*
60381  * Based on:
60382  * Ext JS Library 1.1.1
60383  * Copyright(c) 2006-2007, Ext JS, LLC.
60384  *
60385  * Originally Released Under LGPL - original licence link has changed is not relivant.
60386  *
60387  * Fork - LGPL
60388  * <script type="text/javascript">
60389  */
60390
60391
60392 /**
60393  * @class Roo.XTemplate
60394  * @extends Roo.Template
60395  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60396 <pre><code>
60397 var t = new Roo.XTemplate(
60398         '&lt;select name="{name}"&gt;',
60399                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60400         '&lt;/select&gt;'
60401 );
60402  
60403 // then append, applying the master template values
60404  </code></pre>
60405  *
60406  * Supported features:
60407  *
60408  *  Tags:
60409
60410 <pre><code>
60411       {a_variable} - output encoded.
60412       {a_variable.format:("Y-m-d")} - call a method on the variable
60413       {a_variable:raw} - unencoded output
60414       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60415       {a_variable:this.method_on_template(...)} - call a method on the template object.
60416  
60417 </code></pre>
60418  *  The tpl tag:
60419 <pre><code>
60420         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60421         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60422         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60423         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60424   
60425         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60426         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60427 </code></pre>
60428  *      
60429  */
60430 Roo.XTemplate = function()
60431 {
60432     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60433     if (this.html) {
60434         this.compile();
60435     }
60436 };
60437
60438
60439 Roo.extend(Roo.XTemplate, Roo.Template, {
60440
60441     /**
60442      * The various sub templates
60443      */
60444     tpls : false,
60445     /**
60446      *
60447      * basic tag replacing syntax
60448      * WORD:WORD()
60449      *
60450      * // you can fake an object call by doing this
60451      *  x.t:(test,tesT) 
60452      * 
60453      */
60454     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60455
60456     /**
60457      * compile the template
60458      *
60459      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60460      *
60461      */
60462     compile: function()
60463     {
60464         var s = this.html;
60465      
60466         s = ['<tpl>', s, '</tpl>'].join('');
60467     
60468         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60469             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60470             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60471             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60472             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60473             m,
60474             id     = 0,
60475             tpls   = [];
60476     
60477         while(true == !!(m = s.match(re))){
60478             var forMatch   = m[0].match(nameRe),
60479                 ifMatch   = m[0].match(ifRe),
60480                 execMatch   = m[0].match(execRe),
60481                 namedMatch   = m[0].match(namedRe),
60482                 
60483                 exp  = null, 
60484                 fn   = null,
60485                 exec = null,
60486                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60487                 
60488             if (ifMatch) {
60489                 // if - puts fn into test..
60490                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60491                 if(exp){
60492                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60493                 }
60494             }
60495             
60496             if (execMatch) {
60497                 // exec - calls a function... returns empty if true is  returned.
60498                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60499                 if(exp){
60500                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60501                 }
60502             }
60503             
60504             
60505             if (name) {
60506                 // for = 
60507                 switch(name){
60508                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60509                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60510                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60511                 }
60512             }
60513             var uid = namedMatch ? namedMatch[1] : id;
60514             
60515             
60516             tpls.push({
60517                 id:     namedMatch ? namedMatch[1] : id,
60518                 target: name,
60519                 exec:   exec,
60520                 test:   fn,
60521                 body:   m[1] || ''
60522             });
60523             if (namedMatch) {
60524                 s = s.replace(m[0], '');
60525             } else { 
60526                 s = s.replace(m[0], '{xtpl'+ id + '}');
60527             }
60528             ++id;
60529         }
60530         this.tpls = [];
60531         for(var i = tpls.length-1; i >= 0; --i){
60532             this.compileTpl(tpls[i]);
60533             this.tpls[tpls[i].id] = tpls[i];
60534         }
60535         this.master = tpls[tpls.length-1];
60536         return this;
60537     },
60538     /**
60539      * same as applyTemplate, except it's done to one of the subTemplates
60540      * when using named templates, you can do:
60541      *
60542      * var str = pl.applySubTemplate('your-name', values);
60543      *
60544      * 
60545      * @param {Number} id of the template
60546      * @param {Object} values to apply to template
60547      * @param {Object} parent (normaly the instance of this object)
60548      */
60549     applySubTemplate : function(id, values, parent)
60550     {
60551         
60552         
60553         var t = this.tpls[id];
60554         
60555         
60556         try { 
60557             if(t.test && !t.test.call(this, values, parent)){
60558                 return '';
60559             }
60560         } catch(e) {
60561             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60562             Roo.log(e.toString());
60563             Roo.log(t.test);
60564             return ''
60565         }
60566         try { 
60567             
60568             if(t.exec && t.exec.call(this, values, parent)){
60569                 return '';
60570             }
60571         } catch(e) {
60572             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60573             Roo.log(e.toString());
60574             Roo.log(t.exec);
60575             return ''
60576         }
60577         try {
60578             var vs = t.target ? t.target.call(this, values, parent) : values;
60579             parent = t.target ? values : parent;
60580             if(t.target && vs instanceof Array){
60581                 var buf = [];
60582                 for(var i = 0, len = vs.length; i < len; i++){
60583                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60584                 }
60585                 return buf.join('');
60586             }
60587             return t.compiled.call(this, vs, parent);
60588         } catch (e) {
60589             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60590             Roo.log(e.toString());
60591             Roo.log(t.compiled);
60592             return '';
60593         }
60594     },
60595
60596     compileTpl : function(tpl)
60597     {
60598         var fm = Roo.util.Format;
60599         var useF = this.disableFormats !== true;
60600         var sep = Roo.isGecko ? "+" : ",";
60601         var undef = function(str) {
60602             Roo.log("Property not found :"  + str);
60603             return '';
60604         };
60605         
60606         var fn = function(m, name, format, args)
60607         {
60608             //Roo.log(arguments);
60609             args = args ? args.replace(/\\'/g,"'") : args;
60610             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60611             if (typeof(format) == 'undefined') {
60612                 format= 'htmlEncode';
60613             }
60614             if (format == 'raw' ) {
60615                 format = false;
60616             }
60617             
60618             if(name.substr(0, 4) == 'xtpl'){
60619                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60620             }
60621             
60622             // build an array of options to determine if value is undefined..
60623             
60624             // basically get 'xxxx.yyyy' then do
60625             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60626             //    (function () { Roo.log("Property not found"); return ''; })() :
60627             //    ......
60628             
60629             var udef_ar = [];
60630             var lookfor = '';
60631             Roo.each(name.split('.'), function(st) {
60632                 lookfor += (lookfor.length ? '.': '') + st;
60633                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60634             });
60635             
60636             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60637             
60638             
60639             if(format && useF){
60640                 
60641                 args = args ? ',' + args : "";
60642                  
60643                 if(format.substr(0, 5) != "this."){
60644                     format = "fm." + format + '(';
60645                 }else{
60646                     format = 'this.call("'+ format.substr(5) + '", ';
60647                     args = ", values";
60648                 }
60649                 
60650                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
60651             }
60652              
60653             if (args.length) {
60654                 // called with xxyx.yuu:(test,test)
60655                 // change to ()
60656                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
60657             }
60658             // raw.. - :raw modifier..
60659             return "'"+ sep + udef_st  + name + ")"+sep+"'";
60660             
60661         };
60662         var body;
60663         // branched to use + in gecko and [].join() in others
60664         if(Roo.isGecko){
60665             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
60666                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
60667                     "';};};";
60668         }else{
60669             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
60670             body.push(tpl.body.replace(/(\r\n|\n)/g,
60671                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
60672             body.push("'].join('');};};");
60673             body = body.join('');
60674         }
60675         
60676         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
60677        
60678         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
60679         eval(body);
60680         
60681         return this;
60682     },
60683
60684     applyTemplate : function(values){
60685         return this.master.compiled.call(this, values, {});
60686         //var s = this.subs;
60687     },
60688
60689     apply : function(){
60690         return this.applyTemplate.apply(this, arguments);
60691     }
60692
60693  });
60694
60695 Roo.XTemplate.from = function(el){
60696     el = Roo.getDom(el);
60697     return new Roo.XTemplate(el.value || el.innerHTML);
60698 };