fix size formating
[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      * equals
1021      * @param {Array} o The array to compare to
1022      * @returns {Boolean} true if the same
1023      */
1024     equals : function(b)
1025     {
1026         // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1027         if (this === b) {
1028             return true;
1029          }
1030         if (b == null) {
1031             return false;
1032         }
1033         if (this.length !== b.length) {
1034             return false;
1035         }
1036       
1037         // sort?? a.sort().equals(b.sort());
1038       
1039         for (var i = 0; i < this.length; ++i) {
1040             if (this[i] !== b[i]) {
1041                 return false;
1042             }
1043         }
1044         return true;
1045     }
1046 });
1047
1048
1049  
1050 /*
1051  * Based on:
1052  * Ext JS Library 1.1.1
1053  * Copyright(c) 2006-2007, Ext JS, LLC.
1054  *
1055  * Originally Released Under LGPL - original licence link has changed is not relivant.
1056  *
1057  * Fork - LGPL
1058  * <script type="text/javascript">
1059  */
1060
1061 /**
1062  * @class Date
1063  *
1064  * The date parsing and format syntax is a subset of
1065  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1066  * supported will provide results equivalent to their PHP versions.
1067  *
1068  * Following is the list of all currently supported formats:
1069  *<pre>
1070 Sample date:
1071 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1072
1073 Format  Output      Description
1074 ------  ----------  --------------------------------------------------------------
1075   d      10         Day of the month, 2 digits with leading zeros
1076   D      Wed        A textual representation of a day, three letters
1077   j      10         Day of the month without leading zeros
1078   l      Wednesday  A full textual representation of the day of the week
1079   S      th         English ordinal day of month suffix, 2 chars (use with j)
1080   w      3          Numeric representation of the day of the week
1081   z      9          The julian date, or day of the year (0-365)
1082   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1083   F      January    A full textual representation of the month
1084   m      01         Numeric representation of a month, with leading zeros
1085   M      Jan        Month name abbreviation, three letters
1086   n      1          Numeric representation of a month, without leading zeros
1087   t      31         Number of days in the given month
1088   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1089   Y      2007       A full numeric representation of a year, 4 digits
1090   y      07         A two digit representation of a year
1091   a      pm         Lowercase Ante meridiem and Post meridiem
1092   A      PM         Uppercase Ante meridiem and Post meridiem
1093   g      3          12-hour format of an hour without leading zeros
1094   G      15         24-hour format of an hour without leading zeros
1095   h      03         12-hour format of an hour with leading zeros
1096   H      15         24-hour format of an hour with leading zeros
1097   i      05         Minutes with leading zeros
1098   s      01         Seconds, with leading zeros
1099   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1100   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1101   T      CST        Timezone setting of the machine running the code
1102   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1103 </pre>
1104  *
1105  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1106  * <pre><code>
1107 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1108 document.write(dt.format('Y-m-d'));                         //2007-01-10
1109 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1110 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
1111  </code></pre>
1112  *
1113  * Here are some standard date/time patterns that you might find helpful.  They
1114  * are not part of the source of Date.js, but to use them you can simply copy this
1115  * block of code into any script that is included after Date.js and they will also become
1116  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1117  * <pre><code>
1118 Date.patterns = {
1119     ISO8601Long:"Y-m-d H:i:s",
1120     ISO8601Short:"Y-m-d",
1121     ShortDate: "n/j/Y",
1122     LongDate: "l, F d, Y",
1123     FullDateTime: "l, F d, Y g:i:s A",
1124     MonthDay: "F d",
1125     ShortTime: "g:i A",
1126     LongTime: "g:i:s A",
1127     SortableDateTime: "Y-m-d\\TH:i:s",
1128     UniversalSortableDateTime: "Y-m-d H:i:sO",
1129     YearMonth: "F, Y"
1130 };
1131 </code></pre>
1132  *
1133  * Example usage:
1134  * <pre><code>
1135 var dt = new Date();
1136 document.write(dt.format(Date.patterns.ShortDate));
1137  </code></pre>
1138  */
1139
1140 /*
1141  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1142  * They generate precompiled functions from date formats instead of parsing and
1143  * processing the pattern every time you format a date.  These functions are available
1144  * on every Date object (any javascript function).
1145  *
1146  * The original article and download are here:
1147  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1148  *
1149  */
1150  
1151  
1152  // was in core
1153 /**
1154  Returns the number of milliseconds between this date and date
1155  @param {Date} date (optional) Defaults to now
1156  @return {Number} The diff in milliseconds
1157  @member Date getElapsed
1158  */
1159 Date.prototype.getElapsed = function(date) {
1160         return Math.abs((date || new Date()).getTime()-this.getTime());
1161 };
1162 // was in date file..
1163
1164
1165 // private
1166 Date.parseFunctions = {count:0};
1167 // private
1168 Date.parseRegexes = [];
1169 // private
1170 Date.formatFunctions = {count:0};
1171
1172 // private
1173 Date.prototype.dateFormat = function(format) {
1174     if (Date.formatFunctions[format] == null) {
1175         Date.createNewFormat(format);
1176     }
1177     var func = Date.formatFunctions[format];
1178     return this[func]();
1179 };
1180
1181
1182 /**
1183  * Formats a date given the supplied format string
1184  * @param {String} format The format string
1185  * @return {String} The formatted date
1186  * @method
1187  */
1188 Date.prototype.format = Date.prototype.dateFormat;
1189
1190 // private
1191 Date.createNewFormat = function(format) {
1192     var funcName = "format" + Date.formatFunctions.count++;
1193     Date.formatFunctions[format] = funcName;
1194     var code = "Date.prototype." + funcName + " = function(){return ";
1195     var special = false;
1196     var ch = '';
1197     for (var i = 0; i < format.length; ++i) {
1198         ch = format.charAt(i);
1199         if (!special && ch == "\\") {
1200             special = true;
1201         }
1202         else if (special) {
1203             special = false;
1204             code += "'" + String.escape(ch) + "' + ";
1205         }
1206         else {
1207             code += Date.getFormatCode(ch);
1208         }
1209     }
1210     /** eval:var:zzzzzzzzzzzzz */
1211     eval(code.substring(0, code.length - 3) + ";}");
1212 };
1213
1214 // private
1215 Date.getFormatCode = function(character) {
1216     switch (character) {
1217     case "d":
1218         return "String.leftPad(this.getDate(), 2, '0') + ";
1219     case "D":
1220         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1221     case "j":
1222         return "this.getDate() + ";
1223     case "l":
1224         return "Date.dayNames[this.getDay()] + ";
1225     case "S":
1226         return "this.getSuffix() + ";
1227     case "w":
1228         return "this.getDay() + ";
1229     case "z":
1230         return "this.getDayOfYear() + ";
1231     case "W":
1232         return "this.getWeekOfYear() + ";
1233     case "F":
1234         return "Date.monthNames[this.getMonth()] + ";
1235     case "m":
1236         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1237     case "M":
1238         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1239     case "n":
1240         return "(this.getMonth() + 1) + ";
1241     case "t":
1242         return "this.getDaysInMonth() + ";
1243     case "L":
1244         return "(this.isLeapYear() ? 1 : 0) + ";
1245     case "Y":
1246         return "this.getFullYear() + ";
1247     case "y":
1248         return "('' + this.getFullYear()).substring(2, 4) + ";
1249     case "a":
1250         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1251     case "A":
1252         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1253     case "g":
1254         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1255     case "G":
1256         return "this.getHours() + ";
1257     case "h":
1258         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1259     case "H":
1260         return "String.leftPad(this.getHours(), 2, '0') + ";
1261     case "i":
1262         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1263     case "s":
1264         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1265     case "O":
1266         return "this.getGMTOffset() + ";
1267     case "P":
1268         return "this.getGMTColonOffset() + ";
1269     case "T":
1270         return "this.getTimezone() + ";
1271     case "Z":
1272         return "(this.getTimezoneOffset() * -60) + ";
1273     default:
1274         return "'" + String.escape(character) + "' + ";
1275     }
1276 };
1277
1278 /**
1279  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1280  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1281  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1282  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1283  * string or the parse operation will fail.
1284  * Example Usage:
1285 <pre><code>
1286 //dt = Fri May 25 2007 (current date)
1287 var dt = new Date();
1288
1289 //dt = Thu May 25 2006 (today's month/day in 2006)
1290 dt = Date.parseDate("2006", "Y");
1291
1292 //dt = Sun Jan 15 2006 (all date parts specified)
1293 dt = Date.parseDate("2006-1-15", "Y-m-d");
1294
1295 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1296 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1297 </code></pre>
1298  * @param {String} input The unparsed date as a string
1299  * @param {String} format The format the date is in
1300  * @return {Date} The parsed date
1301  * @static
1302  */
1303 Date.parseDate = function(input, format) {
1304     if (Date.parseFunctions[format] == null) {
1305         Date.createParser(format);
1306     }
1307     var func = Date.parseFunctions[format];
1308     return Date[func](input);
1309 };
1310 /**
1311  * @private
1312  */
1313
1314 Date.createParser = function(format) {
1315     var funcName = "parse" + Date.parseFunctions.count++;
1316     var regexNum = Date.parseRegexes.length;
1317     var currentGroup = 1;
1318     Date.parseFunctions[format] = funcName;
1319
1320     var code = "Date." + funcName + " = function(input){\n"
1321         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1322         + "var d = new Date();\n"
1323         + "y = d.getFullYear();\n"
1324         + "m = d.getMonth();\n"
1325         + "d = d.getDate();\n"
1326         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1327         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1328         + "if (results && results.length > 0) {";
1329     var regex = "";
1330
1331     var special = false;
1332     var ch = '';
1333     for (var i = 0; i < format.length; ++i) {
1334         ch = format.charAt(i);
1335         if (!special && ch == "\\") {
1336             special = true;
1337         }
1338         else if (special) {
1339             special = false;
1340             regex += String.escape(ch);
1341         }
1342         else {
1343             var obj = Date.formatCodeToRegex(ch, currentGroup);
1344             currentGroup += obj.g;
1345             regex += obj.s;
1346             if (obj.g && obj.c) {
1347                 code += obj.c;
1348             }
1349         }
1350     }
1351
1352     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1353         + "{v = new Date(y, m, d, h, i, s);}\n"
1354         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1355         + "{v = new Date(y, m, d, h, i);}\n"
1356         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1357         + "{v = new Date(y, m, d, h);}\n"
1358         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1359         + "{v = new Date(y, m, d);}\n"
1360         + "else if (y >= 0 && m >= 0)\n"
1361         + "{v = new Date(y, m);}\n"
1362         + "else if (y >= 0)\n"
1363         + "{v = new Date(y);}\n"
1364         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1365         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1366         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1367         + ";}";
1368
1369     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1370     /** eval:var:zzzzzzzzzzzzz */
1371     eval(code);
1372 };
1373
1374 // private
1375 Date.formatCodeToRegex = function(character, currentGroup) {
1376     switch (character) {
1377     case "D":
1378         return {g:0,
1379         c:null,
1380         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1381     case "j":
1382         return {g:1,
1383             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1384             s:"(\\d{1,2})"}; // day of month without leading zeroes
1385     case "d":
1386         return {g:1,
1387             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1388             s:"(\\d{2})"}; // day of month with leading zeroes
1389     case "l":
1390         return {g:0,
1391             c:null,
1392             s:"(?:" + Date.dayNames.join("|") + ")"};
1393     case "S":
1394         return {g:0,
1395             c:null,
1396             s:"(?:st|nd|rd|th)"};
1397     case "w":
1398         return {g:0,
1399             c:null,
1400             s:"\\d"};
1401     case "z":
1402         return {g:0,
1403             c:null,
1404             s:"(?:\\d{1,3})"};
1405     case "W":
1406         return {g:0,
1407             c:null,
1408             s:"(?:\\d{2})"};
1409     case "F":
1410         return {g:1,
1411             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1412             s:"(" + Date.monthNames.join("|") + ")"};
1413     case "M":
1414         return {g:1,
1415             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1416             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1417     case "n":
1418         return {g:1,
1419             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1420             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1421     case "m":
1422         return {g:1,
1423             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1424             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1425     case "t":
1426         return {g:0,
1427             c:null,
1428             s:"\\d{1,2}"};
1429     case "L":
1430         return {g:0,
1431             c:null,
1432             s:"(?:1|0)"};
1433     case "Y":
1434         return {g:1,
1435             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1436             s:"(\\d{4})"};
1437     case "y":
1438         return {g:1,
1439             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1440                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1441             s:"(\\d{1,2})"};
1442     case "a":
1443         return {g:1,
1444             c:"if (results[" + currentGroup + "] == 'am') {\n"
1445                 + "if (h == 12) { h = 0; }\n"
1446                 + "} else { if (h < 12) { h += 12; }}",
1447             s:"(am|pm)"};
1448     case "A":
1449         return {g:1,
1450             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1451                 + "if (h == 12) { h = 0; }\n"
1452                 + "} else { if (h < 12) { h += 12; }}",
1453             s:"(AM|PM)"};
1454     case "g":
1455     case "G":
1456         return {g:1,
1457             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1458             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1459     case "h":
1460     case "H":
1461         return {g:1,
1462             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1463             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1464     case "i":
1465         return {g:1,
1466             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1467             s:"(\\d{2})"};
1468     case "s":
1469         return {g:1,
1470             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1471             s:"(\\d{2})"};
1472     case "O":
1473         return {g:1,
1474             c:[
1475                 "o = results[", currentGroup, "];\n",
1476                 "var sn = o.substring(0,1);\n", // get + / - sign
1477                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1478                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1479                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1480                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1481             ].join(""),
1482             s:"([+\-]\\d{2,4})"};
1483     
1484     
1485     case "P":
1486         return {g:1,
1487                 c:[
1488                    "o = results[", currentGroup, "];\n",
1489                    "var sn = o.substring(0,1);\n",
1490                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1491                    "var mn = o.substring(4,6) % 60;\n",
1492                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1493                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1494             ].join(""),
1495             s:"([+\-]\\d{4})"};
1496     case "T":
1497         return {g:0,
1498             c:null,
1499             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1500     case "Z":
1501         return {g:1,
1502             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1503                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1504             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1505     default:
1506         return {g:0,
1507             c:null,
1508             s:String.escape(character)};
1509     }
1510 };
1511
1512 /**
1513  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1514  * @return {String} The abbreviated timezone name (e.g. 'CST')
1515  */
1516 Date.prototype.getTimezone = function() {
1517     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1518 };
1519
1520 /**
1521  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1522  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1523  */
1524 Date.prototype.getGMTOffset = function() {
1525     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1526         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1527         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1528 };
1529
1530 /**
1531  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1532  * @return {String} 2-characters representing hours and 2-characters representing minutes
1533  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1534  */
1535 Date.prototype.getGMTColonOffset = function() {
1536         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1537                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1538                 + ":"
1539                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1540 }
1541
1542 /**
1543  * Get the numeric day number of the year, adjusted for leap year.
1544  * @return {Number} 0 through 364 (365 in leap years)
1545  */
1546 Date.prototype.getDayOfYear = function() {
1547     var num = 0;
1548     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1549     for (var i = 0; i < this.getMonth(); ++i) {
1550         num += Date.daysInMonth[i];
1551     }
1552     return num + this.getDate() - 1;
1553 };
1554
1555 /**
1556  * Get the string representation of the numeric week number of the year
1557  * (equivalent to the format specifier 'W').
1558  * @return {String} '00' through '52'
1559  */
1560 Date.prototype.getWeekOfYear = function() {
1561     // Skip to Thursday of this week
1562     var now = this.getDayOfYear() + (4 - this.getDay());
1563     // Find the first Thursday of the year
1564     var jan1 = new Date(this.getFullYear(), 0, 1);
1565     var then = (7 - jan1.getDay() + 4);
1566     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1567 };
1568
1569 /**
1570  * Whether or not the current date is in a leap year.
1571  * @return {Boolean} True if the current date is in a leap year, else false
1572  */
1573 Date.prototype.isLeapYear = function() {
1574     var year = this.getFullYear();
1575     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1576 };
1577
1578 /**
1579  * Get the first day of the current month, adjusted for leap year.  The returned value
1580  * is the numeric day index within the week (0-6) which can be used in conjunction with
1581  * the {@link #monthNames} array to retrieve the textual day name.
1582  * Example:
1583  *<pre><code>
1584 var dt = new Date('1/10/2007');
1585 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1586 </code></pre>
1587  * @return {Number} The day number (0-6)
1588  */
1589 Date.prototype.getFirstDayOfMonth = function() {
1590     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1591     return (day < 0) ? (day + 7) : day;
1592 };
1593
1594 /**
1595  * Get the last day of the current month, adjusted for leap year.  The returned value
1596  * is the numeric day index within the week (0-6) which can be used in conjunction with
1597  * the {@link #monthNames} array to retrieve the textual day name.
1598  * Example:
1599  *<pre><code>
1600 var dt = new Date('1/10/2007');
1601 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1602 </code></pre>
1603  * @return {Number} The day number (0-6)
1604  */
1605 Date.prototype.getLastDayOfMonth = function() {
1606     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1607     return (day < 0) ? (day + 7) : day;
1608 };
1609
1610
1611 /**
1612  * Get the first date of this date's month
1613  * @return {Date}
1614  */
1615 Date.prototype.getFirstDateOfMonth = function() {
1616     return new Date(this.getFullYear(), this.getMonth(), 1);
1617 };
1618
1619 /**
1620  * Get the last date of this date's month
1621  * @return {Date}
1622  */
1623 Date.prototype.getLastDateOfMonth = function() {
1624     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1625 };
1626 /**
1627  * Get the number of days in the current month, adjusted for leap year.
1628  * @return {Number} The number of days in the month
1629  */
1630 Date.prototype.getDaysInMonth = function() {
1631     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1632     return Date.daysInMonth[this.getMonth()];
1633 };
1634
1635 /**
1636  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1637  * @return {String} 'st, 'nd', 'rd' or 'th'
1638  */
1639 Date.prototype.getSuffix = function() {
1640     switch (this.getDate()) {
1641         case 1:
1642         case 21:
1643         case 31:
1644             return "st";
1645         case 2:
1646         case 22:
1647             return "nd";
1648         case 3:
1649         case 23:
1650             return "rd";
1651         default:
1652             return "th";
1653     }
1654 };
1655
1656 // private
1657 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1658
1659 /**
1660  * An array of textual month names.
1661  * Override these values for international dates, for example...
1662  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1663  * @type Array
1664  * @static
1665  */
1666 Date.monthNames =
1667    ["January",
1668     "February",
1669     "March",
1670     "April",
1671     "May",
1672     "June",
1673     "July",
1674     "August",
1675     "September",
1676     "October",
1677     "November",
1678     "December"];
1679
1680 /**
1681  * An array of textual day names.
1682  * Override these values for international dates, for example...
1683  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1684  * @type Array
1685  * @static
1686  */
1687 Date.dayNames =
1688    ["Sunday",
1689     "Monday",
1690     "Tuesday",
1691     "Wednesday",
1692     "Thursday",
1693     "Friday",
1694     "Saturday"];
1695
1696 // private
1697 Date.y2kYear = 50;
1698 // private
1699 Date.monthNumbers = {
1700     Jan:0,
1701     Feb:1,
1702     Mar:2,
1703     Apr:3,
1704     May:4,
1705     Jun:5,
1706     Jul:6,
1707     Aug:7,
1708     Sep:8,
1709     Oct:9,
1710     Nov:10,
1711     Dec:11};
1712
1713 /**
1714  * Creates and returns a new Date instance with the exact same date value as the called instance.
1715  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1716  * variable will also be changed.  When the intention is to create a new variable that will not
1717  * modify the original instance, you should create a clone.
1718  *
1719  * Example of correctly cloning a date:
1720  * <pre><code>
1721 //wrong way:
1722 var orig = new Date('10/1/2006');
1723 var copy = orig;
1724 copy.setDate(5);
1725 document.write(orig);  //returns 'Thu Oct 05 2006'!
1726
1727 //correct way:
1728 var orig = new Date('10/1/2006');
1729 var copy = orig.clone();
1730 copy.setDate(5);
1731 document.write(orig);  //returns 'Thu Oct 01 2006'
1732 </code></pre>
1733  * @return {Date} The new Date instance
1734  */
1735 Date.prototype.clone = function() {
1736         return new Date(this.getTime());
1737 };
1738
1739 /**
1740  * Clears any time information from this date
1741  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1742  @return {Date} this or the clone
1743  */
1744 Date.prototype.clearTime = function(clone){
1745     if(clone){
1746         return this.clone().clearTime();
1747     }
1748     this.setHours(0);
1749     this.setMinutes(0);
1750     this.setSeconds(0);
1751     this.setMilliseconds(0);
1752     return this;
1753 };
1754
1755 // private
1756 // safari setMonth is broken -- check that this is only donw once...
1757 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1758     Date.brokenSetMonth = Date.prototype.setMonth;
1759         Date.prototype.setMonth = function(num){
1760                 if(num <= -1){
1761                         var n = Math.ceil(-num);
1762                         var back_year = Math.ceil(n/12);
1763                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1764                         this.setFullYear(this.getFullYear() - back_year);
1765                         return Date.brokenSetMonth.call(this, month);
1766                 } else {
1767                         return Date.brokenSetMonth.apply(this, arguments);
1768                 }
1769         };
1770 }
1771
1772 /** Date interval constant 
1773 * @static 
1774 * @type String */
1775 Date.MILLI = "ms";
1776 /** Date interval constant 
1777 * @static 
1778 * @type String */
1779 Date.SECOND = "s";
1780 /** Date interval constant 
1781 * @static 
1782 * @type String */
1783 Date.MINUTE = "mi";
1784 /** Date interval constant 
1785 * @static 
1786 * @type String */
1787 Date.HOUR = "h";
1788 /** Date interval constant 
1789 * @static 
1790 * @type String */
1791 Date.DAY = "d";
1792 /** Date interval constant 
1793 * @static 
1794 * @type String */
1795 Date.MONTH = "mo";
1796 /** Date interval constant 
1797 * @static 
1798 * @type String */
1799 Date.YEAR = "y";
1800
1801 /**
1802  * Provides a convenient method of performing basic date arithmetic.  This method
1803  * does not modify the Date instance being called - it creates and returns
1804  * a new Date instance containing the resulting date value.
1805  *
1806  * Examples:
1807  * <pre><code>
1808 //Basic usage:
1809 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1810 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1811
1812 //Negative values will subtract correctly:
1813 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1814 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1815
1816 //You can even chain several calls together in one line!
1817 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1818 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1819  </code></pre>
1820  *
1821  * @param {String} interval   A valid date interval enum value
1822  * @param {Number} value      The amount to add to the current date
1823  * @return {Date} The new Date instance
1824  */
1825 Date.prototype.add = function(interval, value){
1826   var d = this.clone();
1827   if (!interval || value === 0) { return d; }
1828   switch(interval.toLowerCase()){
1829     case Date.MILLI:
1830       d.setMilliseconds(this.getMilliseconds() + value);
1831       break;
1832     case Date.SECOND:
1833       d.setSeconds(this.getSeconds() + value);
1834       break;
1835     case Date.MINUTE:
1836       d.setMinutes(this.getMinutes() + value);
1837       break;
1838     case Date.HOUR:
1839       d.setHours(this.getHours() + value);
1840       break;
1841     case Date.DAY:
1842       d.setDate(this.getDate() + value);
1843       break;
1844     case Date.MONTH:
1845       var day = this.getDate();
1846       if(day > 28){
1847           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1848       }
1849       d.setDate(day);
1850       d.setMonth(this.getMonth() + value);
1851       break;
1852     case Date.YEAR:
1853       d.setFullYear(this.getFullYear() + value);
1854       break;
1855   }
1856   return d;
1857 };
1858 /*
1859  * Based on:
1860  * Ext JS Library 1.1.1
1861  * Copyright(c) 2006-2007, Ext JS, LLC.
1862  *
1863  * Originally Released Under LGPL - original licence link has changed is not relivant.
1864  *
1865  * Fork - LGPL
1866  * <script type="text/javascript">
1867  */
1868
1869 /**
1870  * @class Roo.lib.Dom
1871  * @static
1872  * 
1873  * Dom utils (from YIU afaik)
1874  * 
1875  **/
1876 Roo.lib.Dom = {
1877     /**
1878      * Get the view width
1879      * @param {Boolean} full True will get the full document, otherwise it's the view width
1880      * @return {Number} The width
1881      */
1882      
1883     getViewWidth : function(full) {
1884         return full ? this.getDocumentWidth() : this.getViewportWidth();
1885     },
1886     /**
1887      * Get the view height
1888      * @param {Boolean} full True will get the full document, otherwise it's the view height
1889      * @return {Number} The height
1890      */
1891     getViewHeight : function(full) {
1892         return full ? this.getDocumentHeight() : this.getViewportHeight();
1893     },
1894
1895     getDocumentHeight: function() {
1896         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1897         return Math.max(scrollHeight, this.getViewportHeight());
1898     },
1899
1900     getDocumentWidth: function() {
1901         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1902         return Math.max(scrollWidth, this.getViewportWidth());
1903     },
1904
1905     getViewportHeight: function() {
1906         var height = self.innerHeight;
1907         var mode = document.compatMode;
1908
1909         if ((mode || Roo.isIE) && !Roo.isOpera) {
1910             height = (mode == "CSS1Compat") ?
1911                      document.documentElement.clientHeight :
1912                      document.body.clientHeight;
1913         }
1914
1915         return height;
1916     },
1917
1918     getViewportWidth: function() {
1919         var width = self.innerWidth;
1920         var mode = document.compatMode;
1921
1922         if (mode || Roo.isIE) {
1923             width = (mode == "CSS1Compat") ?
1924                     document.documentElement.clientWidth :
1925                     document.body.clientWidth;
1926         }
1927         return width;
1928     },
1929
1930     isAncestor : function(p, c) {
1931         p = Roo.getDom(p);
1932         c = Roo.getDom(c);
1933         if (!p || !c) {
1934             return false;
1935         }
1936
1937         if (p.contains && !Roo.isSafari) {
1938             return p.contains(c);
1939         } else if (p.compareDocumentPosition) {
1940             return !!(p.compareDocumentPosition(c) & 16);
1941         } else {
1942             var parent = c.parentNode;
1943             while (parent) {
1944                 if (parent == p) {
1945                     return true;
1946                 }
1947                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1948                     return false;
1949                 }
1950                 parent = parent.parentNode;
1951             }
1952             return false;
1953         }
1954     },
1955
1956     getRegion : function(el) {
1957         return Roo.lib.Region.getRegion(el);
1958     },
1959
1960     getY : function(el) {
1961         return this.getXY(el)[1];
1962     },
1963
1964     getX : function(el) {
1965         return this.getXY(el)[0];
1966     },
1967
1968     getXY : function(el) {
1969         var p, pe, b, scroll, bd = document.body;
1970         el = Roo.getDom(el);
1971         var fly = Roo.lib.AnimBase.fly;
1972         if (el.getBoundingClientRect) {
1973             b = el.getBoundingClientRect();
1974             scroll = fly(document).getScroll();
1975             return [b.left + scroll.left, b.top + scroll.top];
1976         }
1977         var x = 0, y = 0;
1978
1979         p = el;
1980
1981         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1982
1983         while (p) {
1984
1985             x += p.offsetLeft;
1986             y += p.offsetTop;
1987
1988             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1989                 hasAbsolute = true;
1990             }
1991
1992             if (Roo.isGecko) {
1993                 pe = fly(p);
1994
1995                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1996                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1997
1998
1999                 x += bl;
2000                 y += bt;
2001
2002
2003                 if (p != el && pe.getStyle('overflow') != 'visible') {
2004                     x += bl;
2005                     y += bt;
2006                 }
2007             }
2008             p = p.offsetParent;
2009         }
2010
2011         if (Roo.isSafari && hasAbsolute) {
2012             x -= bd.offsetLeft;
2013             y -= bd.offsetTop;
2014         }
2015
2016         if (Roo.isGecko && !hasAbsolute) {
2017             var dbd = fly(bd);
2018             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2019             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2020         }
2021
2022         p = el.parentNode;
2023         while (p && p != bd) {
2024             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2025                 x -= p.scrollLeft;
2026                 y -= p.scrollTop;
2027             }
2028             p = p.parentNode;
2029         }
2030         return [x, y];
2031     },
2032  
2033   
2034
2035
2036     setXY : function(el, xy) {
2037         el = Roo.fly(el, '_setXY');
2038         el.position();
2039         var pts = el.translatePoints(xy);
2040         if (xy[0] !== false) {
2041             el.dom.style.left = pts.left + "px";
2042         }
2043         if (xy[1] !== false) {
2044             el.dom.style.top = pts.top + "px";
2045         }
2046     },
2047
2048     setX : function(el, x) {
2049         this.setXY(el, [x, false]);
2050     },
2051
2052     setY : function(el, y) {
2053         this.setXY(el, [false, y]);
2054     }
2055 };
2056 /*
2057  * Portions of this file are based on pieces of Yahoo User Interface Library
2058  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2059  * YUI licensed under the BSD License:
2060  * http://developer.yahoo.net/yui/license.txt
2061  * <script type="text/javascript">
2062  *
2063  */
2064
2065 Roo.lib.Event = function() {
2066     var loadComplete = false;
2067     var listeners = [];
2068     var unloadListeners = [];
2069     var retryCount = 0;
2070     var onAvailStack = [];
2071     var counter = 0;
2072     var lastError = null;
2073
2074     return {
2075         POLL_RETRYS: 200,
2076         POLL_INTERVAL: 20,
2077         EL: 0,
2078         TYPE: 1,
2079         FN: 2,
2080         WFN: 3,
2081         OBJ: 3,
2082         ADJ_SCOPE: 4,
2083         _interval: null,
2084
2085         startInterval: function() {
2086             if (!this._interval) {
2087                 var self = this;
2088                 var callback = function() {
2089                     self._tryPreloadAttach();
2090                 };
2091                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2092
2093             }
2094         },
2095
2096         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2097             onAvailStack.push({ id:         p_id,
2098                 fn:         p_fn,
2099                 obj:        p_obj,
2100                 override:   p_override,
2101                 checkReady: false    });
2102
2103             retryCount = this.POLL_RETRYS;
2104             this.startInterval();
2105         },
2106
2107
2108         addListener: function(el, eventName, fn) {
2109             el = Roo.getDom(el);
2110             if (!el || !fn) {
2111                 return false;
2112             }
2113
2114             if ("unload" == eventName) {
2115                 unloadListeners[unloadListeners.length] =
2116                 [el, eventName, fn];
2117                 return true;
2118             }
2119
2120             var wrappedFn = function(e) {
2121                 return fn(Roo.lib.Event.getEvent(e));
2122             };
2123
2124             var li = [el, eventName, fn, wrappedFn];
2125
2126             var index = listeners.length;
2127             listeners[index] = li;
2128
2129             this.doAdd(el, eventName, wrappedFn, false);
2130             return true;
2131
2132         },
2133
2134
2135         removeListener: function(el, eventName, fn) {
2136             var i, len;
2137
2138             el = Roo.getDom(el);
2139
2140             if(!fn) {
2141                 return this.purgeElement(el, false, eventName);
2142             }
2143
2144
2145             if ("unload" == eventName) {
2146
2147                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2148                     var li = unloadListeners[i];
2149                     if (li &&
2150                         li[0] == el &&
2151                         li[1] == eventName &&
2152                         li[2] == fn) {
2153                         unloadListeners.splice(i, 1);
2154                         return true;
2155                     }
2156                 }
2157
2158                 return false;
2159             }
2160
2161             var cacheItem = null;
2162
2163
2164             var index = arguments[3];
2165
2166             if ("undefined" == typeof index) {
2167                 index = this._getCacheIndex(el, eventName, fn);
2168             }
2169
2170             if (index >= 0) {
2171                 cacheItem = listeners[index];
2172             }
2173
2174             if (!el || !cacheItem) {
2175                 return false;
2176             }
2177
2178             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2179
2180             delete listeners[index][this.WFN];
2181             delete listeners[index][this.FN];
2182             listeners.splice(index, 1);
2183
2184             return true;
2185
2186         },
2187
2188
2189         getTarget: function(ev, resolveTextNode) {
2190             ev = ev.browserEvent || ev;
2191             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2192             var t = ev.target || ev.srcElement;
2193             return this.resolveTextNode(t);
2194         },
2195
2196
2197         resolveTextNode: function(node) {
2198             if (Roo.isSafari && node && 3 == node.nodeType) {
2199                 return node.parentNode;
2200             } else {
2201                 return node;
2202             }
2203         },
2204
2205
2206         getPageX: function(ev) {
2207             ev = ev.browserEvent || ev;
2208             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2209             var x = ev.pageX;
2210             if (!x && 0 !== x) {
2211                 x = ev.clientX || 0;
2212
2213                 if (Roo.isIE) {
2214                     x += this.getScroll()[1];
2215                 }
2216             }
2217
2218             return x;
2219         },
2220
2221
2222         getPageY: function(ev) {
2223             ev = ev.browserEvent || ev;
2224             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2225             var y = ev.pageY;
2226             if (!y && 0 !== y) {
2227                 y = ev.clientY || 0;
2228
2229                 if (Roo.isIE) {
2230                     y += this.getScroll()[0];
2231                 }
2232             }
2233
2234
2235             return y;
2236         },
2237
2238
2239         getXY: function(ev) {
2240             ev = ev.browserEvent || ev;
2241             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2242             return [this.getPageX(ev), this.getPageY(ev)];
2243         },
2244
2245
2246         getRelatedTarget: function(ev) {
2247             ev = ev.browserEvent || ev;
2248             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2249             var t = ev.relatedTarget;
2250             if (!t) {
2251                 if (ev.type == "mouseout") {
2252                     t = ev.toElement;
2253                 } else if (ev.type == "mouseover") {
2254                     t = ev.fromElement;
2255                 }
2256             }
2257
2258             return this.resolveTextNode(t);
2259         },
2260
2261
2262         getTime: function(ev) {
2263             ev = ev.browserEvent || ev;
2264             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2265             if (!ev.time) {
2266                 var t = new Date().getTime();
2267                 try {
2268                     ev.time = t;
2269                 } catch(ex) {
2270                     this.lastError = ex;
2271                     return t;
2272                 }
2273             }
2274
2275             return ev.time;
2276         },
2277
2278
2279         stopEvent: function(ev) {
2280             this.stopPropagation(ev);
2281             this.preventDefault(ev);
2282         },
2283
2284
2285         stopPropagation: function(ev) {
2286             ev = ev.browserEvent || ev;
2287             if (ev.stopPropagation) {
2288                 ev.stopPropagation();
2289             } else {
2290                 ev.cancelBubble = true;
2291             }
2292         },
2293
2294
2295         preventDefault: function(ev) {
2296             ev = ev.browserEvent || ev;
2297             if(ev.preventDefault) {
2298                 ev.preventDefault();
2299             } else {
2300                 ev.returnValue = false;
2301             }
2302         },
2303
2304
2305         getEvent: function(e) {
2306             var ev = e || window.event;
2307             if (!ev) {
2308                 var c = this.getEvent.caller;
2309                 while (c) {
2310                     ev = c.arguments[0];
2311                     if (ev && Event == ev.constructor) {
2312                         break;
2313                     }
2314                     c = c.caller;
2315                 }
2316             }
2317             return ev;
2318         },
2319
2320
2321         getCharCode: function(ev) {
2322             ev = ev.browserEvent || ev;
2323             return ev.charCode || ev.keyCode || 0;
2324         },
2325
2326
2327         _getCacheIndex: function(el, eventName, fn) {
2328             for (var i = 0,len = listeners.length; i < len; ++i) {
2329                 var li = listeners[i];
2330                 if (li &&
2331                     li[this.FN] == fn &&
2332                     li[this.EL] == el &&
2333                     li[this.TYPE] == eventName) {
2334                     return i;
2335                 }
2336             }
2337
2338             return -1;
2339         },
2340
2341
2342         elCache: {},
2343
2344
2345         getEl: function(id) {
2346             return document.getElementById(id);
2347         },
2348
2349
2350         clearCache: function() {
2351         },
2352
2353
2354         _load: function(e) {
2355             loadComplete = true;
2356             var EU = Roo.lib.Event;
2357
2358
2359             if (Roo.isIE) {
2360                 EU.doRemove(window, "load", EU._load);
2361             }
2362         },
2363
2364
2365         _tryPreloadAttach: function() {
2366
2367             if (this.locked) {
2368                 return false;
2369             }
2370
2371             this.locked = true;
2372
2373
2374             var tryAgain = !loadComplete;
2375             if (!tryAgain) {
2376                 tryAgain = (retryCount > 0);
2377             }
2378
2379
2380             var notAvail = [];
2381             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2382                 var item = onAvailStack[i];
2383                 if (item) {
2384                     var el = this.getEl(item.id);
2385
2386                     if (el) {
2387                         if (!item.checkReady ||
2388                             loadComplete ||
2389                             el.nextSibling ||
2390                             (document && document.body)) {
2391
2392                             var scope = el;
2393                             if (item.override) {
2394                                 if (item.override === true) {
2395                                     scope = item.obj;
2396                                 } else {
2397                                     scope = item.override;
2398                                 }
2399                             }
2400                             item.fn.call(scope, item.obj);
2401                             onAvailStack[i] = null;
2402                         }
2403                     } else {
2404                         notAvail.push(item);
2405                     }
2406                 }
2407             }
2408
2409             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2410
2411             if (tryAgain) {
2412
2413                 this.startInterval();
2414             } else {
2415                 clearInterval(this._interval);
2416                 this._interval = null;
2417             }
2418
2419             this.locked = false;
2420
2421             return true;
2422
2423         },
2424
2425
2426         purgeElement: function(el, recurse, eventName) {
2427             var elListeners = this.getListeners(el, eventName);
2428             if (elListeners) {
2429                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2430                     var l = elListeners[i];
2431                     this.removeListener(el, l.type, l.fn);
2432                 }
2433             }
2434
2435             if (recurse && el && el.childNodes) {
2436                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2437                     this.purgeElement(el.childNodes[i], recurse, eventName);
2438                 }
2439             }
2440         },
2441
2442
2443         getListeners: function(el, eventName) {
2444             var results = [], searchLists;
2445             if (!eventName) {
2446                 searchLists = [listeners, unloadListeners];
2447             } else if (eventName == "unload") {
2448                 searchLists = [unloadListeners];
2449             } else {
2450                 searchLists = [listeners];
2451             }
2452
2453             for (var j = 0; j < searchLists.length; ++j) {
2454                 var searchList = searchLists[j];
2455                 if (searchList && searchList.length > 0) {
2456                     for (var i = 0,len = searchList.length; i < len; ++i) {
2457                         var l = searchList[i];
2458                         if (l && l[this.EL] === el &&
2459                             (!eventName || eventName === l[this.TYPE])) {
2460                             results.push({
2461                                 type:   l[this.TYPE],
2462                                 fn:     l[this.FN],
2463                                 obj:    l[this.OBJ],
2464                                 adjust: l[this.ADJ_SCOPE],
2465                                 index:  i
2466                             });
2467                         }
2468                     }
2469                 }
2470             }
2471
2472             return (results.length) ? results : null;
2473         },
2474
2475
2476         _unload: function(e) {
2477
2478             var EU = Roo.lib.Event, i, j, l, len, index;
2479
2480             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2481                 l = unloadListeners[i];
2482                 if (l) {
2483                     var scope = window;
2484                     if (l[EU.ADJ_SCOPE]) {
2485                         if (l[EU.ADJ_SCOPE] === true) {
2486                             scope = l[EU.OBJ];
2487                         } else {
2488                             scope = l[EU.ADJ_SCOPE];
2489                         }
2490                     }
2491                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2492                     unloadListeners[i] = null;
2493                     l = null;
2494                     scope = null;
2495                 }
2496             }
2497
2498             unloadListeners = null;
2499
2500             if (listeners && listeners.length > 0) {
2501                 j = listeners.length;
2502                 while (j) {
2503                     index = j - 1;
2504                     l = listeners[index];
2505                     if (l) {
2506                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2507                                 l[EU.FN], index);
2508                     }
2509                     j = j - 1;
2510                 }
2511                 l = null;
2512
2513                 EU.clearCache();
2514             }
2515
2516             EU.doRemove(window, "unload", EU._unload);
2517
2518         },
2519
2520
2521         getScroll: function() {
2522             var dd = document.documentElement, db = document.body;
2523             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2524                 return [dd.scrollTop, dd.scrollLeft];
2525             } else if (db) {
2526                 return [db.scrollTop, db.scrollLeft];
2527             } else {
2528                 return [0, 0];
2529             }
2530         },
2531
2532
2533         doAdd: function () {
2534             if (window.addEventListener) {
2535                 return function(el, eventName, fn, capture) {
2536                     el.addEventListener(eventName, fn, (capture));
2537                 };
2538             } else if (window.attachEvent) {
2539                 return function(el, eventName, fn, capture) {
2540                     el.attachEvent("on" + eventName, fn);
2541                 };
2542             } else {
2543                 return function() {
2544                 };
2545             }
2546         }(),
2547
2548
2549         doRemove: function() {
2550             if (window.removeEventListener) {
2551                 return function (el, eventName, fn, capture) {
2552                     el.removeEventListener(eventName, fn, (capture));
2553                 };
2554             } else if (window.detachEvent) {
2555                 return function (el, eventName, fn) {
2556                     el.detachEvent("on" + eventName, fn);
2557                 };
2558             } else {
2559                 return function() {
2560                 };
2561             }
2562         }()
2563     };
2564     
2565 }();
2566 (function() {     
2567    
2568     var E = Roo.lib.Event;
2569     E.on = E.addListener;
2570     E.un = E.removeListener;
2571
2572     if (document && document.body) {
2573         E._load();
2574     } else {
2575         E.doAdd(window, "load", E._load);
2576     }
2577     E.doAdd(window, "unload", E._unload);
2578     E._tryPreloadAttach();
2579 })();
2580
2581 /*
2582  * Portions of this file are based on pieces of Yahoo User Interface Library
2583  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2584  * YUI licensed under the BSD License:
2585  * http://developer.yahoo.net/yui/license.txt
2586  * <script type="text/javascript">
2587  *
2588  */
2589
2590 (function() {
2591     /**
2592      * @class Roo.lib.Ajax
2593      *
2594      */
2595     Roo.lib.Ajax = {
2596         /**
2597          * @static 
2598          */
2599         request : function(method, uri, cb, data, options) {
2600             if(options){
2601                 var hs = options.headers;
2602                 if(hs){
2603                     for(var h in hs){
2604                         if(hs.hasOwnProperty(h)){
2605                             this.initHeader(h, hs[h], false);
2606                         }
2607                     }
2608                 }
2609                 if(options.xmlData){
2610                     this.initHeader('Content-Type', 'text/xml', false);
2611                     method = 'POST';
2612                     data = options.xmlData;
2613                 }
2614             }
2615
2616             return this.asyncRequest(method, uri, cb, data);
2617         },
2618
2619         serializeForm : function(form) {
2620             if(typeof form == 'string') {
2621                 form = (document.getElementById(form) || document.forms[form]);
2622             }
2623
2624             var el, name, val, disabled, data = '', hasSubmit = false;
2625             for (var i = 0; i < form.elements.length; i++) {
2626                 el = form.elements[i];
2627                 disabled = form.elements[i].disabled;
2628                 name = form.elements[i].name;
2629                 val = form.elements[i].value;
2630
2631                 if (!disabled && name){
2632                     switch (el.type)
2633                             {
2634                         case 'select-one':
2635                         case 'select-multiple':
2636                             for (var j = 0; j < el.options.length; j++) {
2637                                 if (el.options[j].selected) {
2638                                     if (Roo.isIE) {
2639                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2640                                     }
2641                                     else {
2642                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2643                                     }
2644                                 }
2645                             }
2646                             break;
2647                         case 'radio':
2648                         case 'checkbox':
2649                             if (el.checked) {
2650                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2651                             }
2652                             break;
2653                         case 'file':
2654
2655                         case undefined:
2656
2657                         case 'reset':
2658
2659                         case 'button':
2660
2661                             break;
2662                         case 'submit':
2663                             if(hasSubmit == false) {
2664                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2665                                 hasSubmit = true;
2666                             }
2667                             break;
2668                         default:
2669                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2670                             break;
2671                     }
2672                 }
2673             }
2674             data = data.substr(0, data.length - 1);
2675             return data;
2676         },
2677
2678         headers:{},
2679
2680         hasHeaders:false,
2681
2682         useDefaultHeader:true,
2683
2684         defaultPostHeader:'application/x-www-form-urlencoded',
2685
2686         useDefaultXhrHeader:true,
2687
2688         defaultXhrHeader:'XMLHttpRequest',
2689
2690         hasDefaultHeaders:true,
2691
2692         defaultHeaders:{},
2693
2694         poll:{},
2695
2696         timeout:{},
2697
2698         pollInterval:50,
2699
2700         transactionId:0,
2701
2702         setProgId:function(id)
2703         {
2704             this.activeX.unshift(id);
2705         },
2706
2707         setDefaultPostHeader:function(b)
2708         {
2709             this.useDefaultHeader = b;
2710         },
2711
2712         setDefaultXhrHeader:function(b)
2713         {
2714             this.useDefaultXhrHeader = b;
2715         },
2716
2717         setPollingInterval:function(i)
2718         {
2719             if (typeof i == 'number' && isFinite(i)) {
2720                 this.pollInterval = i;
2721             }
2722         },
2723
2724         createXhrObject:function(transactionId)
2725         {
2726             var obj,http;
2727             try
2728             {
2729
2730                 http = new XMLHttpRequest();
2731
2732                 obj = { conn:http, tId:transactionId };
2733             }
2734             catch(e)
2735             {
2736                 for (var i = 0; i < this.activeX.length; ++i) {
2737                     try
2738                     {
2739
2740                         http = new ActiveXObject(this.activeX[i]);
2741
2742                         obj = { conn:http, tId:transactionId };
2743                         break;
2744                     }
2745                     catch(e) {
2746                     }
2747                 }
2748             }
2749             finally
2750             {
2751                 return obj;
2752             }
2753         },
2754
2755         getConnectionObject:function()
2756         {
2757             var o;
2758             var tId = this.transactionId;
2759
2760             try
2761             {
2762                 o = this.createXhrObject(tId);
2763                 if (o) {
2764                     this.transactionId++;
2765                 }
2766             }
2767             catch(e) {
2768             }
2769             finally
2770             {
2771                 return o;
2772             }
2773         },
2774
2775         asyncRequest:function(method, uri, callback, postData)
2776         {
2777             var o = this.getConnectionObject();
2778
2779             if (!o) {
2780                 return null;
2781             }
2782             else {
2783                 o.conn.open(method, uri, true);
2784
2785                 if (this.useDefaultXhrHeader) {
2786                     if (!this.defaultHeaders['X-Requested-With']) {
2787                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2788                     }
2789                 }
2790
2791                 if(postData && this.useDefaultHeader){
2792                     this.initHeader('Content-Type', this.defaultPostHeader);
2793                 }
2794
2795                  if (this.hasDefaultHeaders || this.hasHeaders) {
2796                     this.setHeader(o);
2797                 }
2798
2799                 this.handleReadyState(o, callback);
2800                 o.conn.send(postData || null);
2801
2802                 return o;
2803             }
2804         },
2805
2806         handleReadyState:function(o, callback)
2807         {
2808             var oConn = this;
2809
2810             if (callback && callback.timeout) {
2811                 
2812                 this.timeout[o.tId] = window.setTimeout(function() {
2813                     oConn.abort(o, callback, true);
2814                 }, callback.timeout);
2815             }
2816
2817             this.poll[o.tId] = window.setInterval(
2818                     function() {
2819                         if (o.conn && o.conn.readyState == 4) {
2820                             window.clearInterval(oConn.poll[o.tId]);
2821                             delete oConn.poll[o.tId];
2822
2823                             if(callback && callback.timeout) {
2824                                 window.clearTimeout(oConn.timeout[o.tId]);
2825                                 delete oConn.timeout[o.tId];
2826                             }
2827
2828                             oConn.handleTransactionResponse(o, callback);
2829                         }
2830                     }
2831                     , this.pollInterval);
2832         },
2833
2834         handleTransactionResponse:function(o, callback, isAbort)
2835         {
2836
2837             if (!callback) {
2838                 this.releaseObject(o);
2839                 return;
2840             }
2841
2842             var httpStatus, responseObject;
2843
2844             try
2845             {
2846                 if (o.conn.status !== undefined && o.conn.status != 0) {
2847                     httpStatus = o.conn.status;
2848                 }
2849                 else {
2850                     httpStatus = 13030;
2851                 }
2852             }
2853             catch(e) {
2854
2855
2856                 httpStatus = 13030;
2857             }
2858
2859             if (httpStatus >= 200 && httpStatus < 300) {
2860                 responseObject = this.createResponseObject(o, callback.argument);
2861                 if (callback.success) {
2862                     if (!callback.scope) {
2863                         callback.success(responseObject);
2864                     }
2865                     else {
2866
2867
2868                         callback.success.apply(callback.scope, [responseObject]);
2869                     }
2870                 }
2871             }
2872             else {
2873                 switch (httpStatus) {
2874
2875                     case 12002:
2876                     case 12029:
2877                     case 12030:
2878                     case 12031:
2879                     case 12152:
2880                     case 13030:
2881                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2882                         if (callback.failure) {
2883                             if (!callback.scope) {
2884                                 callback.failure(responseObject);
2885                             }
2886                             else {
2887                                 callback.failure.apply(callback.scope, [responseObject]);
2888                             }
2889                         }
2890                         break;
2891                     default:
2892                         responseObject = this.createResponseObject(o, callback.argument);
2893                         if (callback.failure) {
2894                             if (!callback.scope) {
2895                                 callback.failure(responseObject);
2896                             }
2897                             else {
2898                                 callback.failure.apply(callback.scope, [responseObject]);
2899                             }
2900                         }
2901                 }
2902             }
2903
2904             this.releaseObject(o);
2905             responseObject = null;
2906         },
2907
2908         createResponseObject:function(o, callbackArg)
2909         {
2910             var obj = {};
2911             var headerObj = {};
2912
2913             try
2914             {
2915                 var headerStr = o.conn.getAllResponseHeaders();
2916                 var header = headerStr.split('\n');
2917                 for (var i = 0; i < header.length; i++) {
2918                     var delimitPos = header[i].indexOf(':');
2919                     if (delimitPos != -1) {
2920                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2921                     }
2922                 }
2923             }
2924             catch(e) {
2925             }
2926
2927             obj.tId = o.tId;
2928             obj.status = o.conn.status;
2929             obj.statusText = o.conn.statusText;
2930             obj.getResponseHeader = headerObj;
2931             obj.getAllResponseHeaders = headerStr;
2932             obj.responseText = o.conn.responseText;
2933             obj.responseXML = o.conn.responseXML;
2934
2935             if (typeof callbackArg !== undefined) {
2936                 obj.argument = callbackArg;
2937             }
2938
2939             return obj;
2940         },
2941
2942         createExceptionObject:function(tId, callbackArg, isAbort)
2943         {
2944             var COMM_CODE = 0;
2945             var COMM_ERROR = 'communication failure';
2946             var ABORT_CODE = -1;
2947             var ABORT_ERROR = 'transaction aborted';
2948
2949             var obj = {};
2950
2951             obj.tId = tId;
2952             if (isAbort) {
2953                 obj.status = ABORT_CODE;
2954                 obj.statusText = ABORT_ERROR;
2955             }
2956             else {
2957                 obj.status = COMM_CODE;
2958                 obj.statusText = COMM_ERROR;
2959             }
2960
2961             if (callbackArg) {
2962                 obj.argument = callbackArg;
2963             }
2964
2965             return obj;
2966         },
2967
2968         initHeader:function(label, value, isDefault)
2969         {
2970             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2971
2972             if (headerObj[label] === undefined) {
2973                 headerObj[label] = value;
2974             }
2975             else {
2976
2977
2978                 headerObj[label] = value + "," + headerObj[label];
2979             }
2980
2981             if (isDefault) {
2982                 this.hasDefaultHeaders = true;
2983             }
2984             else {
2985                 this.hasHeaders = true;
2986             }
2987         },
2988
2989
2990         setHeader:function(o)
2991         {
2992             if (this.hasDefaultHeaders) {
2993                 for (var prop in this.defaultHeaders) {
2994                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2995                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2996                     }
2997                 }
2998             }
2999
3000             if (this.hasHeaders) {
3001                 for (var prop in this.headers) {
3002                     if (this.headers.hasOwnProperty(prop)) {
3003                         o.conn.setRequestHeader(prop, this.headers[prop]);
3004                     }
3005                 }
3006                 this.headers = {};
3007                 this.hasHeaders = false;
3008             }
3009         },
3010
3011         resetDefaultHeaders:function() {
3012             delete this.defaultHeaders;
3013             this.defaultHeaders = {};
3014             this.hasDefaultHeaders = false;
3015         },
3016
3017         abort:function(o, callback, isTimeout)
3018         {
3019             if(this.isCallInProgress(o)) {
3020                 o.conn.abort();
3021                 window.clearInterval(this.poll[o.tId]);
3022                 delete this.poll[o.tId];
3023                 if (isTimeout) {
3024                     delete this.timeout[o.tId];
3025                 }
3026
3027                 this.handleTransactionResponse(o, callback, true);
3028
3029                 return true;
3030             }
3031             else {
3032                 return false;
3033             }
3034         },
3035
3036
3037         isCallInProgress:function(o)
3038         {
3039             if (o && o.conn) {
3040                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3041             }
3042             else {
3043
3044                 return false;
3045             }
3046         },
3047
3048
3049         releaseObject:function(o)
3050         {
3051
3052             o.conn = null;
3053
3054             o = null;
3055         },
3056
3057         activeX:[
3058         'MSXML2.XMLHTTP.3.0',
3059         'MSXML2.XMLHTTP',
3060         'Microsoft.XMLHTTP'
3061         ]
3062
3063
3064     };
3065 })();/*
3066  * Portions of this file are based on pieces of Yahoo User Interface Library
3067  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3068  * YUI licensed under the BSD License:
3069  * http://developer.yahoo.net/yui/license.txt
3070  * <script type="text/javascript">
3071  *
3072  */
3073
3074 Roo.lib.Region = function(t, r, b, l) {
3075     this.top = t;
3076     this[1] = t;
3077     this.right = r;
3078     this.bottom = b;
3079     this.left = l;
3080     this[0] = l;
3081 };
3082
3083
3084 Roo.lib.Region.prototype = {
3085     contains : function(region) {
3086         return ( region.left >= this.left &&
3087                  region.right <= this.right &&
3088                  region.top >= this.top &&
3089                  region.bottom <= this.bottom    );
3090
3091     },
3092
3093     getArea : function() {
3094         return ( (this.bottom - this.top) * (this.right - this.left) );
3095     },
3096
3097     intersect : function(region) {
3098         var t = Math.max(this.top, region.top);
3099         var r = Math.min(this.right, region.right);
3100         var b = Math.min(this.bottom, region.bottom);
3101         var l = Math.max(this.left, region.left);
3102
3103         if (b >= t && r >= l) {
3104             return new Roo.lib.Region(t, r, b, l);
3105         } else {
3106             return null;
3107         }
3108     },
3109     union : function(region) {
3110         var t = Math.min(this.top, region.top);
3111         var r = Math.max(this.right, region.right);
3112         var b = Math.max(this.bottom, region.bottom);
3113         var l = Math.min(this.left, region.left);
3114
3115         return new Roo.lib.Region(t, r, b, l);
3116     },
3117
3118     adjust : function(t, l, b, r) {
3119         this.top += t;
3120         this.left += l;
3121         this.right += r;
3122         this.bottom += b;
3123         return this;
3124     }
3125 };
3126
3127 Roo.lib.Region.getRegion = function(el) {
3128     var p = Roo.lib.Dom.getXY(el);
3129
3130     var t = p[1];
3131     var r = p[0] + el.offsetWidth;
3132     var b = p[1] + el.offsetHeight;
3133     var l = p[0];
3134
3135     return new Roo.lib.Region(t, r, b, l);
3136 };
3137 /*
3138  * Portions of this file are based on pieces of Yahoo User Interface Library
3139  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3140  * YUI licensed under the BSD License:
3141  * http://developer.yahoo.net/yui/license.txt
3142  * <script type="text/javascript">
3143  *
3144  */
3145 //@@dep Roo.lib.Region
3146
3147
3148 Roo.lib.Point = function(x, y) {
3149     if (x instanceof Array) {
3150         y = x[1];
3151         x = x[0];
3152     }
3153     this.x = this.right = this.left = this[0] = x;
3154     this.y = this.top = this.bottom = this[1] = y;
3155 };
3156
3157 Roo.lib.Point.prototype = new Roo.lib.Region();
3158 /*
3159  * Portions of this file are based on pieces of Yahoo User Interface Library
3160  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3161  * YUI licensed under the BSD License:
3162  * http://developer.yahoo.net/yui/license.txt
3163  * <script type="text/javascript">
3164  *
3165  */
3166  
3167 (function() {   
3168
3169     Roo.lib.Anim = {
3170         scroll : function(el, args, duration, easing, cb, scope) {
3171             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3172         },
3173
3174         motion : function(el, args, duration, easing, cb, scope) {
3175             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3176         },
3177
3178         color : function(el, args, duration, easing, cb, scope) {
3179             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3180         },
3181
3182         run : function(el, args, duration, easing, cb, scope, type) {
3183             type = type || Roo.lib.AnimBase;
3184             if (typeof easing == "string") {
3185                 easing = Roo.lib.Easing[easing];
3186             }
3187             var anim = new type(el, args, duration, easing);
3188             anim.animateX(function() {
3189                 Roo.callback(cb, scope);
3190             });
3191             return anim;
3192         }
3193     };
3194 })();/*
3195  * Portions of this file are based on pieces of Yahoo User Interface Library
3196  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3197  * YUI licensed under the BSD License:
3198  * http://developer.yahoo.net/yui/license.txt
3199  * <script type="text/javascript">
3200  *
3201  */
3202
3203 (function() {    
3204     var libFlyweight;
3205     
3206     function fly(el) {
3207         if (!libFlyweight) {
3208             libFlyweight = new Roo.Element.Flyweight();
3209         }
3210         libFlyweight.dom = el;
3211         return libFlyweight;
3212     }
3213
3214     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3215     
3216    
3217     
3218     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3219         if (el) {
3220             this.init(el, attributes, duration, method);
3221         }
3222     };
3223
3224     Roo.lib.AnimBase.fly = fly;
3225     
3226     
3227     
3228     Roo.lib.AnimBase.prototype = {
3229
3230         toString: function() {
3231             var el = this.getEl();
3232             var id = el.id || el.tagName;
3233             return ("Anim " + id);
3234         },
3235
3236         patterns: {
3237             noNegatives:        /width|height|opacity|padding/i,
3238             offsetAttribute:  /^((width|height)|(top|left))$/,
3239             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3240             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3241         },
3242
3243
3244         doMethod: function(attr, start, end) {
3245             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3246         },
3247
3248
3249         setAttribute: function(attr, val, unit) {
3250             if (this.patterns.noNegatives.test(attr)) {
3251                 val = (val > 0) ? val : 0;
3252             }
3253
3254             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3255         },
3256
3257
3258         getAttribute: function(attr) {
3259             var el = this.getEl();
3260             var val = fly(el).getStyle(attr);
3261
3262             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3263                 return parseFloat(val);
3264             }
3265
3266             var a = this.patterns.offsetAttribute.exec(attr) || [];
3267             var pos = !!( a[3] );
3268             var box = !!( a[2] );
3269
3270
3271             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3272                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3273             } else {
3274                 val = 0;
3275             }
3276
3277             return val;
3278         },
3279
3280
3281         getDefaultUnit: function(attr) {
3282             if (this.patterns.defaultUnit.test(attr)) {
3283                 return 'px';
3284             }
3285
3286             return '';
3287         },
3288
3289         animateX : function(callback, scope) {
3290             var f = function() {
3291                 this.onComplete.removeListener(f);
3292                 if (typeof callback == "function") {
3293                     callback.call(scope || this, this);
3294                 }
3295             };
3296             this.onComplete.addListener(f, this);
3297             this.animate();
3298         },
3299
3300
3301         setRuntimeAttribute: function(attr) {
3302             var start;
3303             var end;
3304             var attributes = this.attributes;
3305
3306             this.runtimeAttributes[attr] = {};
3307
3308             var isset = function(prop) {
3309                 return (typeof prop !== 'undefined');
3310             };
3311
3312             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3313                 return false;
3314             }
3315
3316             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3317
3318
3319             if (isset(attributes[attr]['to'])) {
3320                 end = attributes[attr]['to'];
3321             } else if (isset(attributes[attr]['by'])) {
3322                 if (start.constructor == Array) {
3323                     end = [];
3324                     for (var i = 0, len = start.length; i < len; ++i) {
3325                         end[i] = start[i] + attributes[attr]['by'][i];
3326                     }
3327                 } else {
3328                     end = start + attributes[attr]['by'];
3329                 }
3330             }
3331
3332             this.runtimeAttributes[attr].start = start;
3333             this.runtimeAttributes[attr].end = end;
3334
3335
3336             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3337         },
3338
3339
3340         init: function(el, attributes, duration, method) {
3341
3342             var isAnimated = false;
3343
3344
3345             var startTime = null;
3346
3347
3348             var actualFrames = 0;
3349
3350
3351             el = Roo.getDom(el);
3352
3353
3354             this.attributes = attributes || {};
3355
3356
3357             this.duration = duration || 1;
3358
3359
3360             this.method = method || Roo.lib.Easing.easeNone;
3361
3362
3363             this.useSeconds = true;
3364
3365
3366             this.currentFrame = 0;
3367
3368
3369             this.totalFrames = Roo.lib.AnimMgr.fps;
3370
3371
3372             this.getEl = function() {
3373                 return el;
3374             };
3375
3376
3377             this.isAnimated = function() {
3378                 return isAnimated;
3379             };
3380
3381
3382             this.getStartTime = function() {
3383                 return startTime;
3384             };
3385
3386             this.runtimeAttributes = {};
3387
3388
3389             this.animate = function() {
3390                 if (this.isAnimated()) {
3391                     return false;
3392                 }
3393
3394                 this.currentFrame = 0;
3395
3396                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3397
3398                 Roo.lib.AnimMgr.registerElement(this);
3399             };
3400
3401
3402             this.stop = function(finish) {
3403                 if (finish) {
3404                     this.currentFrame = this.totalFrames;
3405                     this._onTween.fire();
3406                 }
3407                 Roo.lib.AnimMgr.stop(this);
3408             };
3409
3410             var onStart = function() {
3411                 this.onStart.fire();
3412
3413                 this.runtimeAttributes = {};
3414                 for (var attr in this.attributes) {
3415                     this.setRuntimeAttribute(attr);
3416                 }
3417
3418                 isAnimated = true;
3419                 actualFrames = 0;
3420                 startTime = new Date();
3421             };
3422
3423
3424             var onTween = function() {
3425                 var data = {
3426                     duration: new Date() - this.getStartTime(),
3427                     currentFrame: this.currentFrame
3428                 };
3429
3430                 data.toString = function() {
3431                     return (
3432                             'duration: ' + data.duration +
3433                             ', currentFrame: ' + data.currentFrame
3434                             );
3435                 };
3436
3437                 this.onTween.fire(data);
3438
3439                 var runtimeAttributes = this.runtimeAttributes;
3440
3441                 for (var attr in runtimeAttributes) {
3442                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3443                 }
3444
3445                 actualFrames += 1;
3446             };
3447
3448             var onComplete = function() {
3449                 var actual_duration = (new Date() - startTime) / 1000 ;
3450
3451                 var data = {
3452                     duration: actual_duration,
3453                     frames: actualFrames,
3454                     fps: actualFrames / actual_duration
3455                 };
3456
3457                 data.toString = function() {
3458                     return (
3459                             'duration: ' + data.duration +
3460                             ', frames: ' + data.frames +
3461                             ', fps: ' + data.fps
3462                             );
3463                 };
3464
3465                 isAnimated = false;
3466                 actualFrames = 0;
3467                 this.onComplete.fire(data);
3468             };
3469
3470
3471             this._onStart = new Roo.util.Event(this);
3472             this.onStart = new Roo.util.Event(this);
3473             this.onTween = new Roo.util.Event(this);
3474             this._onTween = new Roo.util.Event(this);
3475             this.onComplete = new Roo.util.Event(this);
3476             this._onComplete = new Roo.util.Event(this);
3477             this._onStart.addListener(onStart);
3478             this._onTween.addListener(onTween);
3479             this._onComplete.addListener(onComplete);
3480         }
3481     };
3482 })();
3483 /*
3484  * Portions of this file are based on pieces of Yahoo User Interface Library
3485  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3486  * YUI licensed under the BSD License:
3487  * http://developer.yahoo.net/yui/license.txt
3488  * <script type="text/javascript">
3489  *
3490  */
3491
3492 Roo.lib.AnimMgr = new function() {
3493
3494     var thread = null;
3495
3496
3497     var queue = [];
3498
3499
3500     var tweenCount = 0;
3501
3502
3503     this.fps = 1000;
3504
3505
3506     this.delay = 1;
3507
3508
3509     this.registerElement = function(tween) {
3510         queue[queue.length] = tween;
3511         tweenCount += 1;
3512         tween._onStart.fire();
3513         this.start();
3514     };
3515
3516
3517     this.unRegister = function(tween, index) {
3518         tween._onComplete.fire();
3519         index = index || getIndex(tween);
3520         if (index != -1) {
3521             queue.splice(index, 1);
3522         }
3523
3524         tweenCount -= 1;
3525         if (tweenCount <= 0) {
3526             this.stop();
3527         }
3528     };
3529
3530
3531     this.start = function() {
3532         if (thread === null) {
3533             thread = setInterval(this.run, this.delay);
3534         }
3535     };
3536
3537
3538     this.stop = function(tween) {
3539         if (!tween) {
3540             clearInterval(thread);
3541
3542             for (var i = 0, len = queue.length; i < len; ++i) {
3543                 if (queue[0].isAnimated()) {
3544                     this.unRegister(queue[0], 0);
3545                 }
3546             }
3547
3548             queue = [];
3549             thread = null;
3550             tweenCount = 0;
3551         }
3552         else {
3553             this.unRegister(tween);
3554         }
3555     };
3556
3557
3558     this.run = function() {
3559         for (var i = 0, len = queue.length; i < len; ++i) {
3560             var tween = queue[i];
3561             if (!tween || !tween.isAnimated()) {
3562                 continue;
3563             }
3564
3565             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3566             {
3567                 tween.currentFrame += 1;
3568
3569                 if (tween.useSeconds) {
3570                     correctFrame(tween);
3571                 }
3572                 tween._onTween.fire();
3573             }
3574             else {
3575                 Roo.lib.AnimMgr.stop(tween, i);
3576             }
3577         }
3578     };
3579
3580     var getIndex = function(anim) {
3581         for (var i = 0, len = queue.length; i < len; ++i) {
3582             if (queue[i] == anim) {
3583                 return i;
3584             }
3585         }
3586         return -1;
3587     };
3588
3589
3590     var correctFrame = function(tween) {
3591         var frames = tween.totalFrames;
3592         var frame = tween.currentFrame;
3593         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3594         var elapsed = (new Date() - tween.getStartTime());
3595         var tweak = 0;
3596
3597         if (elapsed < tween.duration * 1000) {
3598             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3599         } else {
3600             tweak = frames - (frame + 1);
3601         }
3602         if (tweak > 0 && isFinite(tweak)) {
3603             if (tween.currentFrame + tweak >= frames) {
3604                 tweak = frames - (frame + 1);
3605             }
3606
3607             tween.currentFrame += tweak;
3608         }
3609     };
3610 };
3611
3612     /*
3613  * Portions of this file are based on pieces of Yahoo User Interface Library
3614  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3615  * YUI licensed under the BSD License:
3616  * http://developer.yahoo.net/yui/license.txt
3617  * <script type="text/javascript">
3618  *
3619  */
3620 Roo.lib.Bezier = new function() {
3621
3622         this.getPosition = function(points, t) {
3623             var n = points.length;
3624             var tmp = [];
3625
3626             for (var i = 0; i < n; ++i) {
3627                 tmp[i] = [points[i][0], points[i][1]];
3628             }
3629
3630             for (var j = 1; j < n; ++j) {
3631                 for (i = 0; i < n - j; ++i) {
3632                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3633                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3634                 }
3635             }
3636
3637             return [ tmp[0][0], tmp[0][1] ];
3638
3639         };
3640     };/*
3641  * Portions of this file are based on pieces of Yahoo User Interface Library
3642  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3643  * YUI licensed under the BSD License:
3644  * http://developer.yahoo.net/yui/license.txt
3645  * <script type="text/javascript">
3646  *
3647  */
3648 (function() {
3649
3650     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3651         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3652     };
3653
3654     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3655
3656     var fly = Roo.lib.AnimBase.fly;
3657     var Y = Roo.lib;
3658     var superclass = Y.ColorAnim.superclass;
3659     var proto = Y.ColorAnim.prototype;
3660
3661     proto.toString = function() {
3662         var el = this.getEl();
3663         var id = el.id || el.tagName;
3664         return ("ColorAnim " + id);
3665     };
3666
3667     proto.patterns.color = /color$/i;
3668     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3669     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3670     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3671     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3672
3673
3674     proto.parseColor = function(s) {
3675         if (s.length == 3) {
3676             return s;
3677         }
3678
3679         var c = this.patterns.hex.exec(s);
3680         if (c && c.length == 4) {
3681             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3682         }
3683
3684         c = this.patterns.rgb.exec(s);
3685         if (c && c.length == 4) {
3686             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3687         }
3688
3689         c = this.patterns.hex3.exec(s);
3690         if (c && c.length == 4) {
3691             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3692         }
3693
3694         return null;
3695     };
3696     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3697     proto.getAttribute = function(attr) {
3698         var el = this.getEl();
3699         if (this.patterns.color.test(attr)) {
3700             var val = fly(el).getStyle(attr);
3701
3702             if (this.patterns.transparent.test(val)) {
3703                 var parent = el.parentNode;
3704                 val = fly(parent).getStyle(attr);
3705
3706                 while (parent && this.patterns.transparent.test(val)) {
3707                     parent = parent.parentNode;
3708                     val = fly(parent).getStyle(attr);
3709                     if (parent.tagName.toUpperCase() == 'HTML') {
3710                         val = '#fff';
3711                     }
3712                 }
3713             }
3714         } else {
3715             val = superclass.getAttribute.call(this, attr);
3716         }
3717
3718         return val;
3719     };
3720     proto.getAttribute = function(attr) {
3721         var el = this.getEl();
3722         if (this.patterns.color.test(attr)) {
3723             var val = fly(el).getStyle(attr);
3724
3725             if (this.patterns.transparent.test(val)) {
3726                 var parent = el.parentNode;
3727                 val = fly(parent).getStyle(attr);
3728
3729                 while (parent && this.patterns.transparent.test(val)) {
3730                     parent = parent.parentNode;
3731                     val = fly(parent).getStyle(attr);
3732                     if (parent.tagName.toUpperCase() == 'HTML') {
3733                         val = '#fff';
3734                     }
3735                 }
3736             }
3737         } else {
3738             val = superclass.getAttribute.call(this, attr);
3739         }
3740
3741         return val;
3742     };
3743
3744     proto.doMethod = function(attr, start, end) {
3745         var val;
3746
3747         if (this.patterns.color.test(attr)) {
3748             val = [];
3749             for (var i = 0, len = start.length; i < len; ++i) {
3750                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3751             }
3752
3753             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3754         }
3755         else {
3756             val = superclass.doMethod.call(this, attr, start, end);
3757         }
3758
3759         return val;
3760     };
3761
3762     proto.setRuntimeAttribute = function(attr) {
3763         superclass.setRuntimeAttribute.call(this, attr);
3764
3765         if (this.patterns.color.test(attr)) {
3766             var attributes = this.attributes;
3767             var start = this.parseColor(this.runtimeAttributes[attr].start);
3768             var end = this.parseColor(this.runtimeAttributes[attr].end);
3769
3770             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3771                 end = this.parseColor(attributes[attr].by);
3772
3773                 for (var i = 0, len = start.length; i < len; ++i) {
3774                     end[i] = start[i] + end[i];
3775                 }
3776             }
3777
3778             this.runtimeAttributes[attr].start = start;
3779             this.runtimeAttributes[attr].end = end;
3780         }
3781     };
3782 })();
3783
3784 /*
3785  * Portions of this file are based on pieces of Yahoo User Interface Library
3786  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3787  * YUI licensed under the BSD License:
3788  * http://developer.yahoo.net/yui/license.txt
3789  * <script type="text/javascript">
3790  *
3791  */
3792 Roo.lib.Easing = {
3793
3794
3795     easeNone: function (t, b, c, d) {
3796         return c * t / d + b;
3797     },
3798
3799
3800     easeIn: function (t, b, c, d) {
3801         return c * (t /= d) * t + b;
3802     },
3803
3804
3805     easeOut: function (t, b, c, d) {
3806         return -c * (t /= d) * (t - 2) + b;
3807     },
3808
3809
3810     easeBoth: function (t, b, c, d) {
3811         if ((t /= d / 2) < 1) {
3812             return c / 2 * t * t + b;
3813         }
3814
3815         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3816     },
3817
3818
3819     easeInStrong: function (t, b, c, d) {
3820         return c * (t /= d) * t * t * t + b;
3821     },
3822
3823
3824     easeOutStrong: function (t, b, c, d) {
3825         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3826     },
3827
3828
3829     easeBothStrong: function (t, b, c, d) {
3830         if ((t /= d / 2) < 1) {
3831             return c / 2 * t * t * t * t + b;
3832         }
3833
3834         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3835     },
3836
3837
3838
3839     elasticIn: function (t, b, c, d, a, p) {
3840         if (t == 0) {
3841             return b;
3842         }
3843         if ((t /= d) == 1) {
3844             return b + c;
3845         }
3846         if (!p) {
3847             p = d * .3;
3848         }
3849
3850         if (!a || a < Math.abs(c)) {
3851             a = c;
3852             var s = p / 4;
3853         }
3854         else {
3855             var s = p / (2 * Math.PI) * Math.asin(c / a);
3856         }
3857
3858         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3859     },
3860
3861
3862     elasticOut: function (t, b, c, d, a, p) {
3863         if (t == 0) {
3864             return b;
3865         }
3866         if ((t /= d) == 1) {
3867             return b + c;
3868         }
3869         if (!p) {
3870             p = d * .3;
3871         }
3872
3873         if (!a || a < Math.abs(c)) {
3874             a = c;
3875             var s = p / 4;
3876         }
3877         else {
3878             var s = p / (2 * Math.PI) * Math.asin(c / a);
3879         }
3880
3881         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3882     },
3883
3884
3885     elasticBoth: function (t, b, c, d, a, p) {
3886         if (t == 0) {
3887             return b;
3888         }
3889
3890         if ((t /= d / 2) == 2) {
3891             return b + c;
3892         }
3893
3894         if (!p) {
3895             p = d * (.3 * 1.5);
3896         }
3897
3898         if (!a || a < Math.abs(c)) {
3899             a = c;
3900             var s = p / 4;
3901         }
3902         else {
3903             var s = p / (2 * Math.PI) * Math.asin(c / a);
3904         }
3905
3906         if (t < 1) {
3907             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3908                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3909         }
3910         return a * Math.pow(2, -10 * (t -= 1)) *
3911                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3912     },
3913
3914
3915
3916     backIn: function (t, b, c, d, s) {
3917         if (typeof s == 'undefined') {
3918             s = 1.70158;
3919         }
3920         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3921     },
3922
3923
3924     backOut: function (t, b, c, d, s) {
3925         if (typeof s == 'undefined') {
3926             s = 1.70158;
3927         }
3928         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3929     },
3930
3931
3932     backBoth: function (t, b, c, d, s) {
3933         if (typeof s == 'undefined') {
3934             s = 1.70158;
3935         }
3936
3937         if ((t /= d / 2 ) < 1) {
3938             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3939         }
3940         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3941     },
3942
3943
3944     bounceIn: function (t, b, c, d) {
3945         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3946     },
3947
3948
3949     bounceOut: function (t, b, c, d) {
3950         if ((t /= d) < (1 / 2.75)) {
3951             return c * (7.5625 * t * t) + b;
3952         } else if (t < (2 / 2.75)) {
3953             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3954         } else if (t < (2.5 / 2.75)) {
3955             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3956         }
3957         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3958     },
3959
3960
3961     bounceBoth: function (t, b, c, d) {
3962         if (t < d / 2) {
3963             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3964         }
3965         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3966     }
3967 };/*
3968  * Portions of this file are based on pieces of Yahoo User Interface Library
3969  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3970  * YUI licensed under the BSD License:
3971  * http://developer.yahoo.net/yui/license.txt
3972  * <script type="text/javascript">
3973  *
3974  */
3975     (function() {
3976         Roo.lib.Motion = function(el, attributes, duration, method) {
3977             if (el) {
3978                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3979             }
3980         };
3981
3982         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3983
3984
3985         var Y = Roo.lib;
3986         var superclass = Y.Motion.superclass;
3987         var proto = Y.Motion.prototype;
3988
3989         proto.toString = function() {
3990             var el = this.getEl();
3991             var id = el.id || el.tagName;
3992             return ("Motion " + id);
3993         };
3994
3995         proto.patterns.points = /^points$/i;
3996
3997         proto.setAttribute = function(attr, val, unit) {
3998             if (this.patterns.points.test(attr)) {
3999                 unit = unit || 'px';
4000                 superclass.setAttribute.call(this, 'left', val[0], unit);
4001                 superclass.setAttribute.call(this, 'top', val[1], unit);
4002             } else {
4003                 superclass.setAttribute.call(this, attr, val, unit);
4004             }
4005         };
4006
4007         proto.getAttribute = function(attr) {
4008             if (this.patterns.points.test(attr)) {
4009                 var val = [
4010                         superclass.getAttribute.call(this, 'left'),
4011                         superclass.getAttribute.call(this, 'top')
4012                         ];
4013             } else {
4014                 val = superclass.getAttribute.call(this, attr);
4015             }
4016
4017             return val;
4018         };
4019
4020         proto.doMethod = function(attr, start, end) {
4021             var val = null;
4022
4023             if (this.patterns.points.test(attr)) {
4024                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4025                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4026             } else {
4027                 val = superclass.doMethod.call(this, attr, start, end);
4028             }
4029             return val;
4030         };
4031
4032         proto.setRuntimeAttribute = function(attr) {
4033             if (this.patterns.points.test(attr)) {
4034                 var el = this.getEl();
4035                 var attributes = this.attributes;
4036                 var start;
4037                 var control = attributes['points']['control'] || [];
4038                 var end;
4039                 var i, len;
4040
4041                 if (control.length > 0 && !(control[0] instanceof Array)) {
4042                     control = [control];
4043                 } else {
4044                     var tmp = [];
4045                     for (i = 0,len = control.length; i < len; ++i) {
4046                         tmp[i] = control[i];
4047                     }
4048                     control = tmp;
4049                 }
4050
4051                 Roo.fly(el).position();
4052
4053                 if (isset(attributes['points']['from'])) {
4054                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4055                 }
4056                 else {
4057                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4058                 }
4059
4060                 start = this.getAttribute('points');
4061
4062
4063                 if (isset(attributes['points']['to'])) {
4064                     end = translateValues.call(this, attributes['points']['to'], start);
4065
4066                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4067                     for (i = 0,len = control.length; i < len; ++i) {
4068                         control[i] = translateValues.call(this, control[i], start);
4069                     }
4070
4071
4072                 } else if (isset(attributes['points']['by'])) {
4073                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4074
4075                     for (i = 0,len = control.length; i < len; ++i) {
4076                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4077                     }
4078                 }
4079
4080                 this.runtimeAttributes[attr] = [start];
4081
4082                 if (control.length > 0) {
4083                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4084                 }
4085
4086                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4087             }
4088             else {
4089                 superclass.setRuntimeAttribute.call(this, attr);
4090             }
4091         };
4092
4093         var translateValues = function(val, start) {
4094             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4095             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4096
4097             return val;
4098         };
4099
4100         var isset = function(prop) {
4101             return (typeof prop !== 'undefined');
4102         };
4103     })();
4104 /*
4105  * Portions of this file are based on pieces of Yahoo User Interface Library
4106  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4107  * YUI licensed under the BSD License:
4108  * http://developer.yahoo.net/yui/license.txt
4109  * <script type="text/javascript">
4110  *
4111  */
4112     (function() {
4113         Roo.lib.Scroll = function(el, attributes, duration, method) {
4114             if (el) {
4115                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4116             }
4117         };
4118
4119         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4120
4121
4122         var Y = Roo.lib;
4123         var superclass = Y.Scroll.superclass;
4124         var proto = Y.Scroll.prototype;
4125
4126         proto.toString = function() {
4127             var el = this.getEl();
4128             var id = el.id || el.tagName;
4129             return ("Scroll " + id);
4130         };
4131
4132         proto.doMethod = function(attr, start, end) {
4133             var val = null;
4134
4135             if (attr == 'scroll') {
4136                 val = [
4137                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4138                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4139                         ];
4140
4141             } else {
4142                 val = superclass.doMethod.call(this, attr, start, end);
4143             }
4144             return val;
4145         };
4146
4147         proto.getAttribute = function(attr) {
4148             var val = null;
4149             var el = this.getEl();
4150
4151             if (attr == 'scroll') {
4152                 val = [ el.scrollLeft, el.scrollTop ];
4153             } else {
4154                 val = superclass.getAttribute.call(this, attr);
4155             }
4156
4157             return val;
4158         };
4159
4160         proto.setAttribute = function(attr, val, unit) {
4161             var el = this.getEl();
4162
4163             if (attr == 'scroll') {
4164                 el.scrollLeft = val[0];
4165                 el.scrollTop = val[1];
4166             } else {
4167                 superclass.setAttribute.call(this, attr, val, unit);
4168             }
4169         };
4170     })();
4171 /*
4172  * Based on:
4173  * Ext JS Library 1.1.1
4174  * Copyright(c) 2006-2007, Ext JS, LLC.
4175  *
4176  * Originally Released Under LGPL - original licence link has changed is not relivant.
4177  *
4178  * Fork - LGPL
4179  * <script type="text/javascript">
4180  */
4181
4182
4183 // nasty IE9 hack - what a pile of crap that is..
4184
4185  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4186     Range.prototype.createContextualFragment = function (html) {
4187         var doc = window.document;
4188         var container = doc.createElement("div");
4189         container.innerHTML = html;
4190         var frag = doc.createDocumentFragment(), n;
4191         while ((n = container.firstChild)) {
4192             frag.appendChild(n);
4193         }
4194         return frag;
4195     };
4196 }
4197
4198 /**
4199  * @class Roo.DomHelper
4200  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4201  * 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>.
4202  * @singleton
4203  */
4204 Roo.DomHelper = function(){
4205     var tempTableEl = null;
4206     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4207     var tableRe = /^table|tbody|tr|td$/i;
4208     var xmlns = {};
4209     // build as innerHTML where available
4210     /** @ignore */
4211     var createHtml = function(o){
4212         if(typeof o == 'string'){
4213             return o;
4214         }
4215         var b = "";
4216         if(!o.tag){
4217             o.tag = "div";
4218         }
4219         b += "<" + o.tag;
4220         for(var attr in o){
4221             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4222             if(attr == "style"){
4223                 var s = o["style"];
4224                 if(typeof s == "function"){
4225                     s = s.call();
4226                 }
4227                 if(typeof s == "string"){
4228                     b += ' style="' + s + '"';
4229                 }else if(typeof s == "object"){
4230                     b += ' style="';
4231                     for(var key in s){
4232                         if(typeof s[key] != "function"){
4233                             b += key + ":" + s[key] + ";";
4234                         }
4235                     }
4236                     b += '"';
4237                 }
4238             }else{
4239                 if(attr == "cls"){
4240                     b += ' class="' + o["cls"] + '"';
4241                 }else if(attr == "htmlFor"){
4242                     b += ' for="' + o["htmlFor"] + '"';
4243                 }else{
4244                     b += " " + attr + '="' + o[attr] + '"';
4245                 }
4246             }
4247         }
4248         if(emptyTags.test(o.tag)){
4249             b += "/>";
4250         }else{
4251             b += ">";
4252             var cn = o.children || o.cn;
4253             if(cn){
4254                 //http://bugs.kde.org/show_bug.cgi?id=71506
4255                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4256                     for(var i = 0, len = cn.length; i < len; i++) {
4257                         b += createHtml(cn[i], b);
4258                     }
4259                 }else{
4260                     b += createHtml(cn, b);
4261                 }
4262             }
4263             if(o.html){
4264                 b += o.html;
4265             }
4266             b += "</" + o.tag + ">";
4267         }
4268         return b;
4269     };
4270
4271     // build as dom
4272     /** @ignore */
4273     var createDom = function(o, parentNode){
4274          
4275         // defininition craeted..
4276         var ns = false;
4277         if (o.ns && o.ns != 'html') {
4278                
4279             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4280                 xmlns[o.ns] = o.xmlns;
4281                 ns = o.xmlns;
4282             }
4283             if (typeof(xmlns[o.ns]) == 'undefined') {
4284                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4285             }
4286             ns = xmlns[o.ns];
4287         }
4288         
4289         
4290         if (typeof(o) == 'string') {
4291             return parentNode.appendChild(document.createTextNode(o));
4292         }
4293         o.tag = o.tag || div;
4294         if (o.ns && Roo.isIE) {
4295             ns = false;
4296             o.tag = o.ns + ':' + o.tag;
4297             
4298         }
4299         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4300         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4301         for(var attr in o){
4302             
4303             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4304                     attr == "style" || typeof o[attr] == "function") { continue; }
4305                     
4306             if(attr=="cls" && Roo.isIE){
4307                 el.className = o["cls"];
4308             }else{
4309                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4310                 else { 
4311                     el[attr] = o[attr];
4312                 }
4313             }
4314         }
4315         Roo.DomHelper.applyStyles(el, o.style);
4316         var cn = o.children || o.cn;
4317         if(cn){
4318             //http://bugs.kde.org/show_bug.cgi?id=71506
4319              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4320                 for(var i = 0, len = cn.length; i < len; i++) {
4321                     createDom(cn[i], el);
4322                 }
4323             }else{
4324                 createDom(cn, el);
4325             }
4326         }
4327         if(o.html){
4328             el.innerHTML = o.html;
4329         }
4330         if(parentNode){
4331            parentNode.appendChild(el);
4332         }
4333         return el;
4334     };
4335
4336     var ieTable = function(depth, s, h, e){
4337         tempTableEl.innerHTML = [s, h, e].join('');
4338         var i = -1, el = tempTableEl;
4339         while(++i < depth){
4340             el = el.firstChild;
4341         }
4342         return el;
4343     };
4344
4345     // kill repeat to save bytes
4346     var ts = '<table>',
4347         te = '</table>',
4348         tbs = ts+'<tbody>',
4349         tbe = '</tbody>'+te,
4350         trs = tbs + '<tr>',
4351         tre = '</tr>'+tbe;
4352
4353     /**
4354      * @ignore
4355      * Nasty code for IE's broken table implementation
4356      */
4357     var insertIntoTable = function(tag, where, el, html){
4358         if(!tempTableEl){
4359             tempTableEl = document.createElement('div');
4360         }
4361         var node;
4362         var before = null;
4363         if(tag == 'td'){
4364             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4365                 return;
4366             }
4367             if(where == 'beforebegin'){
4368                 before = el;
4369                 el = el.parentNode;
4370             } else{
4371                 before = el.nextSibling;
4372                 el = el.parentNode;
4373             }
4374             node = ieTable(4, trs, html, tre);
4375         }
4376         else if(tag == 'tr'){
4377             if(where == 'beforebegin'){
4378                 before = el;
4379                 el = el.parentNode;
4380                 node = ieTable(3, tbs, html, tbe);
4381             } else if(where == 'afterend'){
4382                 before = el.nextSibling;
4383                 el = el.parentNode;
4384                 node = ieTable(3, tbs, html, tbe);
4385             } else{ // INTO a TR
4386                 if(where == 'afterbegin'){
4387                     before = el.firstChild;
4388                 }
4389                 node = ieTable(4, trs, html, tre);
4390             }
4391         } else if(tag == 'tbody'){
4392             if(where == 'beforebegin'){
4393                 before = el;
4394                 el = el.parentNode;
4395                 node = ieTable(2, ts, html, te);
4396             } else if(where == 'afterend'){
4397                 before = el.nextSibling;
4398                 el = el.parentNode;
4399                 node = ieTable(2, ts, html, te);
4400             } else{
4401                 if(where == 'afterbegin'){
4402                     before = el.firstChild;
4403                 }
4404                 node = ieTable(3, tbs, html, tbe);
4405             }
4406         } else{ // TABLE
4407             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4408                 return;
4409             }
4410             if(where == 'afterbegin'){
4411                 before = el.firstChild;
4412             }
4413             node = ieTable(2, ts, html, te);
4414         }
4415         el.insertBefore(node, before);
4416         return node;
4417     };
4418
4419     return {
4420     /** True to force the use of DOM instead of html fragments @type Boolean */
4421     useDom : false,
4422
4423     /**
4424      * Returns the markup for the passed Element(s) config
4425      * @param {Object} o The Dom object spec (and children)
4426      * @return {String}
4427      */
4428     markup : function(o){
4429         return createHtml(o);
4430     },
4431
4432     /**
4433      * Applies a style specification to an element
4434      * @param {String/HTMLElement} el The element to apply styles to
4435      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4436      * a function which returns such a specification.
4437      */
4438     applyStyles : function(el, styles){
4439         if(styles){
4440            el = Roo.fly(el);
4441            if(typeof styles == "string"){
4442                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4443                var matches;
4444                while ((matches = re.exec(styles)) != null){
4445                    el.setStyle(matches[1], matches[2]);
4446                }
4447            }else if (typeof styles == "object"){
4448                for (var style in styles){
4449                   el.setStyle(style, styles[style]);
4450                }
4451            }else if (typeof styles == "function"){
4452                 Roo.DomHelper.applyStyles(el, styles.call());
4453            }
4454         }
4455     },
4456
4457     /**
4458      * Inserts an HTML fragment into the Dom
4459      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4460      * @param {HTMLElement} el The context element
4461      * @param {String} html The HTML fragmenet
4462      * @return {HTMLElement} The new node
4463      */
4464     insertHtml : function(where, el, html){
4465         where = where.toLowerCase();
4466         if(el.insertAdjacentHTML){
4467             if(tableRe.test(el.tagName)){
4468                 var rs;
4469                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4470                     return rs;
4471                 }
4472             }
4473             switch(where){
4474                 case "beforebegin":
4475                     el.insertAdjacentHTML('BeforeBegin', html);
4476                     return el.previousSibling;
4477                 case "afterbegin":
4478                     el.insertAdjacentHTML('AfterBegin', html);
4479                     return el.firstChild;
4480                 case "beforeend":
4481                     el.insertAdjacentHTML('BeforeEnd', html);
4482                     return el.lastChild;
4483                 case "afterend":
4484                     el.insertAdjacentHTML('AfterEnd', html);
4485                     return el.nextSibling;
4486             }
4487             throw 'Illegal insertion point -> "' + where + '"';
4488         }
4489         var range = el.ownerDocument.createRange();
4490         var frag;
4491         switch(where){
4492              case "beforebegin":
4493                 range.setStartBefore(el);
4494                 frag = range.createContextualFragment(html);
4495                 el.parentNode.insertBefore(frag, el);
4496                 return el.previousSibling;
4497              case "afterbegin":
4498                 if(el.firstChild){
4499                     range.setStartBefore(el.firstChild);
4500                     frag = range.createContextualFragment(html);
4501                     el.insertBefore(frag, el.firstChild);
4502                     return el.firstChild;
4503                 }else{
4504                     el.innerHTML = html;
4505                     return el.firstChild;
4506                 }
4507             case "beforeend":
4508                 if(el.lastChild){
4509                     range.setStartAfter(el.lastChild);
4510                     frag = range.createContextualFragment(html);
4511                     el.appendChild(frag);
4512                     return el.lastChild;
4513                 }else{
4514                     el.innerHTML = html;
4515                     return el.lastChild;
4516                 }
4517             case "afterend":
4518                 range.setStartAfter(el);
4519                 frag = range.createContextualFragment(html);
4520                 el.parentNode.insertBefore(frag, el.nextSibling);
4521                 return el.nextSibling;
4522             }
4523             throw 'Illegal insertion point -> "' + where + '"';
4524     },
4525
4526     /**
4527      * Creates new Dom element(s) and inserts them before el
4528      * @param {String/HTMLElement/Element} el The context element
4529      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4530      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4531      * @return {HTMLElement/Roo.Element} The new node
4532      */
4533     insertBefore : function(el, o, returnElement){
4534         return this.doInsert(el, o, returnElement, "beforeBegin");
4535     },
4536
4537     /**
4538      * Creates new Dom element(s) and inserts them after el
4539      * @param {String/HTMLElement/Element} el The context element
4540      * @param {Object} o The Dom object spec (and children)
4541      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4542      * @return {HTMLElement/Roo.Element} The new node
4543      */
4544     insertAfter : function(el, o, returnElement){
4545         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4546     },
4547
4548     /**
4549      * Creates new Dom element(s) and inserts them as the first child of el
4550      * @param {String/HTMLElement/Element} el The context element
4551      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4552      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4553      * @return {HTMLElement/Roo.Element} The new node
4554      */
4555     insertFirst : function(el, o, returnElement){
4556         return this.doInsert(el, o, returnElement, "afterBegin");
4557     },
4558
4559     // private
4560     doInsert : function(el, o, returnElement, pos, sibling){
4561         el = Roo.getDom(el);
4562         var newNode;
4563         if(this.useDom || o.ns){
4564             newNode = createDom(o, null);
4565             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4566         }else{
4567             var html = createHtml(o);
4568             newNode = this.insertHtml(pos, el, html);
4569         }
4570         return returnElement ? Roo.get(newNode, true) : newNode;
4571     },
4572
4573     /**
4574      * Creates new Dom element(s) and appends them to el
4575      * @param {String/HTMLElement/Element} el The context element
4576      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4577      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4578      * @return {HTMLElement/Roo.Element} The new node
4579      */
4580     append : function(el, o, returnElement){
4581         el = Roo.getDom(el);
4582         var newNode;
4583         if(this.useDom || o.ns){
4584             newNode = createDom(o, null);
4585             el.appendChild(newNode);
4586         }else{
4587             var html = createHtml(o);
4588             newNode = this.insertHtml("beforeEnd", el, html);
4589         }
4590         return returnElement ? Roo.get(newNode, true) : newNode;
4591     },
4592
4593     /**
4594      * Creates new Dom element(s) and overwrites the contents of el with them
4595      * @param {String/HTMLElement/Element} el The context element
4596      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4597      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4598      * @return {HTMLElement/Roo.Element} The new node
4599      */
4600     overwrite : function(el, o, returnElement){
4601         el = Roo.getDom(el);
4602         if (o.ns) {
4603           
4604             while (el.childNodes.length) {
4605                 el.removeChild(el.firstChild);
4606             }
4607             createDom(o, el);
4608         } else {
4609             el.innerHTML = createHtml(o);   
4610         }
4611         
4612         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4613     },
4614
4615     /**
4616      * Creates a new Roo.DomHelper.Template from the Dom object spec
4617      * @param {Object} o The Dom object spec (and children)
4618      * @return {Roo.DomHelper.Template} The new template
4619      */
4620     createTemplate : function(o){
4621         var html = createHtml(o);
4622         return new Roo.Template(html);
4623     }
4624     };
4625 }();
4626 /*
4627  * Based on:
4628  * Ext JS Library 1.1.1
4629  * Copyright(c) 2006-2007, Ext JS, LLC.
4630  *
4631  * Originally Released Under LGPL - original licence link has changed is not relivant.
4632  *
4633  * Fork - LGPL
4634  * <script type="text/javascript">
4635  */
4636  
4637 /**
4638 * @class Roo.Template
4639 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4640 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4641 * Usage:
4642 <pre><code>
4643 var t = new Roo.Template({
4644     html :  '&lt;div name="{id}"&gt;' + 
4645         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4646         '&lt;/div&gt;',
4647     myformat: function (value, allValues) {
4648         return 'XX' + value;
4649     }
4650 });
4651 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4652 </code></pre>
4653 * For more information see this blog post with examples:
4654 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4655      - Create Elements using DOM, HTML fragments and Templates</a>. 
4656 * @constructor
4657 * @param {Object} cfg - Configuration object.
4658 */
4659 Roo.Template = function(cfg){
4660     // BC!
4661     if(cfg instanceof Array){
4662         cfg = cfg.join("");
4663     }else if(arguments.length > 1){
4664         cfg = Array.prototype.join.call(arguments, "");
4665     }
4666     
4667     
4668     if (typeof(cfg) == 'object') {
4669         Roo.apply(this,cfg)
4670     } else {
4671         // bc
4672         this.html = cfg;
4673     }
4674     if (this.url) {
4675         this.load();
4676     }
4677     
4678 };
4679 Roo.Template.prototype = {
4680     
4681     /**
4682      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
4683      */
4684     onLoad : false,
4685     
4686     
4687     /**
4688      * @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..
4689      *                    it should be fixed so that template is observable...
4690      */
4691     url : false,
4692     /**
4693      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4694      */
4695     html : '',
4696     
4697     
4698     compiled : false,
4699     loaded : false,
4700     /**
4701      * Returns an HTML fragment of this template with the specified values applied.
4702      * @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'})
4703      * @return {String} The HTML fragment
4704      */
4705     
4706    
4707     
4708     applyTemplate : function(values){
4709         //Roo.log(["applyTemplate", values]);
4710         try {
4711            
4712             if(this.compiled){
4713                 return this.compiled(values);
4714             }
4715             var useF = this.disableFormats !== true;
4716             var fm = Roo.util.Format, tpl = this;
4717             var fn = function(m, name, format, args){
4718                 if(format && useF){
4719                     if(format.substr(0, 5) == "this."){
4720                         return tpl.call(format.substr(5), values[name], values);
4721                     }else{
4722                         if(args){
4723                             // quoted values are required for strings in compiled templates, 
4724                             // but for non compiled we need to strip them
4725                             // quoted reversed for jsmin
4726                             var re = /^\s*['"](.*)["']\s*$/;
4727                             args = args.split(',');
4728                             for(var i = 0, len = args.length; i < len; i++){
4729                                 args[i] = args[i].replace(re, "$1");
4730                             }
4731                             args = [values[name]].concat(args);
4732                         }else{
4733                             args = [values[name]];
4734                         }
4735                         return fm[format].apply(fm, args);
4736                     }
4737                 }else{
4738                     return values[name] !== undefined ? values[name] : "";
4739                 }
4740             };
4741             return this.html.replace(this.re, fn);
4742         } catch (e) {
4743             Roo.log(e);
4744             throw e;
4745         }
4746          
4747     },
4748     
4749     loading : false,
4750       
4751     load : function ()
4752     {
4753          
4754         if (this.loading) {
4755             return;
4756         }
4757         var _t = this;
4758         
4759         this.loading = true;
4760         this.compiled = false;
4761         
4762         var cx = new Roo.data.Connection();
4763         cx.request({
4764             url : this.url,
4765             method : 'GET',
4766             success : function (response) {
4767                 _t.loading = false;
4768                 _t.url = false;
4769                 
4770                 _t.set(response.responseText,true);
4771                 _t.loaded = true;
4772                 if (_t.onLoad) {
4773                     _t.onLoad();
4774                 }
4775              },
4776             failure : function(response) {
4777                 Roo.log("Template failed to load from " + _t.url);
4778                 _t.loading = false;
4779             }
4780         });
4781     },
4782
4783     /**
4784      * Sets the HTML used as the template and optionally compiles it.
4785      * @param {String} html
4786      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4787      * @return {Roo.Template} this
4788      */
4789     set : function(html, compile){
4790         this.html = html;
4791         this.compiled = false;
4792         if(compile){
4793             this.compile();
4794         }
4795         return this;
4796     },
4797     
4798     /**
4799      * True to disable format functions (defaults to false)
4800      * @type Boolean
4801      */
4802     disableFormats : false,
4803     
4804     /**
4805     * The regular expression used to match template variables 
4806     * @type RegExp
4807     * @property 
4808     */
4809     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4810     
4811     /**
4812      * Compiles the template into an internal function, eliminating the RegEx overhead.
4813      * @return {Roo.Template} this
4814      */
4815     compile : function(){
4816         var fm = Roo.util.Format;
4817         var useF = this.disableFormats !== true;
4818         var sep = Roo.isGecko ? "+" : ",";
4819         var fn = function(m, name, format, args){
4820             if(format && useF){
4821                 args = args ? ',' + args : "";
4822                 if(format.substr(0, 5) != "this."){
4823                     format = "fm." + format + '(';
4824                 }else{
4825                     format = 'this.call("'+ format.substr(5) + '", ';
4826                     args = ", values";
4827                 }
4828             }else{
4829                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4830             }
4831             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4832         };
4833         var body;
4834         // branched to use + in gecko and [].join() in others
4835         if(Roo.isGecko){
4836             body = "this.compiled = function(values){ return '" +
4837                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4838                     "';};";
4839         }else{
4840             body = ["this.compiled = function(values){ return ['"];
4841             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4842             body.push("'].join('');};");
4843             body = body.join('');
4844         }
4845         /**
4846          * eval:var:values
4847          * eval:var:fm
4848          */
4849         eval(body);
4850         return this;
4851     },
4852     
4853     // private function used to call members
4854     call : function(fnName, value, allValues){
4855         return this[fnName](value, allValues);
4856     },
4857     
4858     /**
4859      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4860      * @param {String/HTMLElement/Roo.Element} el The context element
4861      * @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'})
4862      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4863      * @return {HTMLElement/Roo.Element} The new node or Element
4864      */
4865     insertFirst: function(el, values, returnElement){
4866         return this.doInsert('afterBegin', el, values, returnElement);
4867     },
4868
4869     /**
4870      * Applies the supplied values to the template and inserts the new node(s) before el.
4871      * @param {String/HTMLElement/Roo.Element} el The context element
4872      * @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'})
4873      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4874      * @return {HTMLElement/Roo.Element} The new node or Element
4875      */
4876     insertBefore: function(el, values, returnElement){
4877         return this.doInsert('beforeBegin', el, values, returnElement);
4878     },
4879
4880     /**
4881      * Applies the supplied values to the template and inserts the new node(s) after el.
4882      * @param {String/HTMLElement/Roo.Element} el The context element
4883      * @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'})
4884      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4885      * @return {HTMLElement/Roo.Element} The new node or Element
4886      */
4887     insertAfter : function(el, values, returnElement){
4888         return this.doInsert('afterEnd', el, values, returnElement);
4889     },
4890     
4891     /**
4892      * Applies the supplied values to the template and appends the new node(s) to el.
4893      * @param {String/HTMLElement/Roo.Element} el The context element
4894      * @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'})
4895      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4896      * @return {HTMLElement/Roo.Element} The new node or Element
4897      */
4898     append : function(el, values, returnElement){
4899         return this.doInsert('beforeEnd', el, values, returnElement);
4900     },
4901
4902     doInsert : function(where, el, values, returnEl){
4903         el = Roo.getDom(el);
4904         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4905         return returnEl ? Roo.get(newNode, true) : newNode;
4906     },
4907
4908     /**
4909      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4910      * @param {String/HTMLElement/Roo.Element} el The context element
4911      * @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'})
4912      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4913      * @return {HTMLElement/Roo.Element} The new node or Element
4914      */
4915     overwrite : function(el, values, returnElement){
4916         el = Roo.getDom(el);
4917         el.innerHTML = this.applyTemplate(values);
4918         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4919     }
4920 };
4921 /**
4922  * Alias for {@link #applyTemplate}
4923  * @method
4924  */
4925 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4926
4927 // backwards compat
4928 Roo.DomHelper.Template = Roo.Template;
4929
4930 /**
4931  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4932  * @param {String/HTMLElement} el A DOM element or its id
4933  * @returns {Roo.Template} The created template
4934  * @static
4935  */
4936 Roo.Template.from = function(el){
4937     el = Roo.getDom(el);
4938     return new Roo.Template(el.value || el.innerHTML);
4939 };/*
4940  * Based on:
4941  * Ext JS Library 1.1.1
4942  * Copyright(c) 2006-2007, Ext JS, LLC.
4943  *
4944  * Originally Released Under LGPL - original licence link has changed is not relivant.
4945  *
4946  * Fork - LGPL
4947  * <script type="text/javascript">
4948  */
4949  
4950
4951 /*
4952  * This is code is also distributed under MIT license for use
4953  * with jQuery and prototype JavaScript libraries.
4954  */
4955 /**
4956  * @class Roo.DomQuery
4957 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).
4958 <p>
4959 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>
4960
4961 <p>
4962 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.
4963 </p>
4964 <h4>Element Selectors:</h4>
4965 <ul class="list">
4966     <li> <b>*</b> any element</li>
4967     <li> <b>E</b> an element with the tag E</li>
4968     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4969     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4970     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4971     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4972 </ul>
4973 <h4>Attribute Selectors:</h4>
4974 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4975 <ul class="list">
4976     <li> <b>E[foo]</b> has an attribute "foo"</li>
4977     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4978     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4979     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4980     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4981     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4982     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4983 </ul>
4984 <h4>Pseudo Classes:</h4>
4985 <ul class="list">
4986     <li> <b>E:first-child</b> E is the first child of its parent</li>
4987     <li> <b>E:last-child</b> E is the last child of its parent</li>
4988     <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>
4989     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4990     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4991     <li> <b>E:only-child</b> E is the only child of its parent</li>
4992     <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>
4993     <li> <b>E:first</b> the first E in the resultset</li>
4994     <li> <b>E:last</b> the last E in the resultset</li>
4995     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4996     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4997     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4998     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4999     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5000     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5001     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5002     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5003     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5004 </ul>
5005 <h4>CSS Value Selectors:</h4>
5006 <ul class="list">
5007     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5008     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5009     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5010     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5011     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5012     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5013 </ul>
5014  * @singleton
5015  */
5016 Roo.DomQuery = function(){
5017     var cache = {}, simpleCache = {}, valueCache = {};
5018     var nonSpace = /\S/;
5019     var trimRe = /^\s+|\s+$/g;
5020     var tplRe = /\{(\d+)\}/g;
5021     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5022     var tagTokenRe = /^(#)?([\w-\*]+)/;
5023     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5024
5025     function child(p, index){
5026         var i = 0;
5027         var n = p.firstChild;
5028         while(n){
5029             if(n.nodeType == 1){
5030                if(++i == index){
5031                    return n;
5032                }
5033             }
5034             n = n.nextSibling;
5035         }
5036         return null;
5037     };
5038
5039     function next(n){
5040         while((n = n.nextSibling) && n.nodeType != 1);
5041         return n;
5042     };
5043
5044     function prev(n){
5045         while((n = n.previousSibling) && n.nodeType != 1);
5046         return n;
5047     };
5048
5049     function children(d){
5050         var n = d.firstChild, ni = -1;
5051             while(n){
5052                 var nx = n.nextSibling;
5053                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5054                     d.removeChild(n);
5055                 }else{
5056                     n.nodeIndex = ++ni;
5057                 }
5058                 n = nx;
5059             }
5060             return this;
5061         };
5062
5063     function byClassName(c, a, v){
5064         if(!v){
5065             return c;
5066         }
5067         var r = [], ri = -1, cn;
5068         for(var i = 0, ci; ci = c[i]; i++){
5069             if((' '+ci.className+' ').indexOf(v) != -1){
5070                 r[++ri] = ci;
5071             }
5072         }
5073         return r;
5074     };
5075
5076     function attrValue(n, attr){
5077         if(!n.tagName && typeof n.length != "undefined"){
5078             n = n[0];
5079         }
5080         if(!n){
5081             return null;
5082         }
5083         if(attr == "for"){
5084             return n.htmlFor;
5085         }
5086         if(attr == "class" || attr == "className"){
5087             return n.className;
5088         }
5089         return n.getAttribute(attr) || n[attr];
5090
5091     };
5092
5093     function getNodes(ns, mode, tagName){
5094         var result = [], ri = -1, cs;
5095         if(!ns){
5096             return result;
5097         }
5098         tagName = tagName || "*";
5099         if(typeof ns.getElementsByTagName != "undefined"){
5100             ns = [ns];
5101         }
5102         if(!mode){
5103             for(var i = 0, ni; ni = ns[i]; i++){
5104                 cs = ni.getElementsByTagName(tagName);
5105                 for(var j = 0, ci; ci = cs[j]; j++){
5106                     result[++ri] = ci;
5107                 }
5108             }
5109         }else if(mode == "/" || mode == ">"){
5110             var utag = tagName.toUpperCase();
5111             for(var i = 0, ni, cn; ni = ns[i]; i++){
5112                 cn = ni.children || ni.childNodes;
5113                 for(var j = 0, cj; cj = cn[j]; j++){
5114                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5115                         result[++ri] = cj;
5116                     }
5117                 }
5118             }
5119         }else if(mode == "+"){
5120             var utag = tagName.toUpperCase();
5121             for(var i = 0, n; n = ns[i]; i++){
5122                 while((n = n.nextSibling) && n.nodeType != 1);
5123                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5124                     result[++ri] = n;
5125                 }
5126             }
5127         }else if(mode == "~"){
5128             for(var i = 0, n; n = ns[i]; i++){
5129                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5130                 if(n){
5131                     result[++ri] = n;
5132                 }
5133             }
5134         }
5135         return result;
5136     };
5137
5138     function concat(a, b){
5139         if(b.slice){
5140             return a.concat(b);
5141         }
5142         for(var i = 0, l = b.length; i < l; i++){
5143             a[a.length] = b[i];
5144         }
5145         return a;
5146     }
5147
5148     function byTag(cs, tagName){
5149         if(cs.tagName || cs == document){
5150             cs = [cs];
5151         }
5152         if(!tagName){
5153             return cs;
5154         }
5155         var r = [], ri = -1;
5156         tagName = tagName.toLowerCase();
5157         for(var i = 0, ci; ci = cs[i]; i++){
5158             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5159                 r[++ri] = ci;
5160             }
5161         }
5162         return r;
5163     };
5164
5165     function byId(cs, attr, id){
5166         if(cs.tagName || cs == document){
5167             cs = [cs];
5168         }
5169         if(!id){
5170             return cs;
5171         }
5172         var r = [], ri = -1;
5173         for(var i = 0,ci; ci = cs[i]; i++){
5174             if(ci && ci.id == id){
5175                 r[++ri] = ci;
5176                 return r;
5177             }
5178         }
5179         return r;
5180     };
5181
5182     function byAttribute(cs, attr, value, op, custom){
5183         var r = [], ri = -1, st = custom=="{";
5184         var f = Roo.DomQuery.operators[op];
5185         for(var i = 0, ci; ci = cs[i]; i++){
5186             var a;
5187             if(st){
5188                 a = Roo.DomQuery.getStyle(ci, attr);
5189             }
5190             else if(attr == "class" || attr == "className"){
5191                 a = ci.className;
5192             }else if(attr == "for"){
5193                 a = ci.htmlFor;
5194             }else if(attr == "href"){
5195                 a = ci.getAttribute("href", 2);
5196             }else{
5197                 a = ci.getAttribute(attr);
5198             }
5199             if((f && f(a, value)) || (!f && a)){
5200                 r[++ri] = ci;
5201             }
5202         }
5203         return r;
5204     };
5205
5206     function byPseudo(cs, name, value){
5207         return Roo.DomQuery.pseudos[name](cs, value);
5208     };
5209
5210     // This is for IE MSXML which does not support expandos.
5211     // IE runs the same speed using setAttribute, however FF slows way down
5212     // and Safari completely fails so they need to continue to use expandos.
5213     var isIE = window.ActiveXObject ? true : false;
5214
5215     // this eval is stop the compressor from
5216     // renaming the variable to something shorter
5217     
5218     /** eval:var:batch */
5219     var batch = 30803; 
5220
5221     var key = 30803;
5222
5223     function nodupIEXml(cs){
5224         var d = ++key;
5225         cs[0].setAttribute("_nodup", d);
5226         var r = [cs[0]];
5227         for(var i = 1, len = cs.length; i < len; i++){
5228             var c = cs[i];
5229             if(!c.getAttribute("_nodup") != d){
5230                 c.setAttribute("_nodup", d);
5231                 r[r.length] = c;
5232             }
5233         }
5234         for(var i = 0, len = cs.length; i < len; i++){
5235             cs[i].removeAttribute("_nodup");
5236         }
5237         return r;
5238     }
5239
5240     function nodup(cs){
5241         if(!cs){
5242             return [];
5243         }
5244         var len = cs.length, c, i, r = cs, cj, ri = -1;
5245         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5246             return cs;
5247         }
5248         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5249             return nodupIEXml(cs);
5250         }
5251         var d = ++key;
5252         cs[0]._nodup = d;
5253         for(i = 1; c = cs[i]; i++){
5254             if(c._nodup != d){
5255                 c._nodup = d;
5256             }else{
5257                 r = [];
5258                 for(var j = 0; j < i; j++){
5259                     r[++ri] = cs[j];
5260                 }
5261                 for(j = i+1; cj = cs[j]; j++){
5262                     if(cj._nodup != d){
5263                         cj._nodup = d;
5264                         r[++ri] = cj;
5265                     }
5266                 }
5267                 return r;
5268             }
5269         }
5270         return r;
5271     }
5272
5273     function quickDiffIEXml(c1, c2){
5274         var d = ++key;
5275         for(var i = 0, len = c1.length; i < len; i++){
5276             c1[i].setAttribute("_qdiff", d);
5277         }
5278         var r = [];
5279         for(var i = 0, len = c2.length; i < len; i++){
5280             if(c2[i].getAttribute("_qdiff") != d){
5281                 r[r.length] = c2[i];
5282             }
5283         }
5284         for(var i = 0, len = c1.length; i < len; i++){
5285            c1[i].removeAttribute("_qdiff");
5286         }
5287         return r;
5288     }
5289
5290     function quickDiff(c1, c2){
5291         var len1 = c1.length;
5292         if(!len1){
5293             return c2;
5294         }
5295         if(isIE && c1[0].selectSingleNode){
5296             return quickDiffIEXml(c1, c2);
5297         }
5298         var d = ++key;
5299         for(var i = 0; i < len1; i++){
5300             c1[i]._qdiff = d;
5301         }
5302         var r = [];
5303         for(var i = 0, len = c2.length; i < len; i++){
5304             if(c2[i]._qdiff != d){
5305                 r[r.length] = c2[i];
5306             }
5307         }
5308         return r;
5309     }
5310
5311     function quickId(ns, mode, root, id){
5312         if(ns == root){
5313            var d = root.ownerDocument || root;
5314            return d.getElementById(id);
5315         }
5316         ns = getNodes(ns, mode, "*");
5317         return byId(ns, null, id);
5318     }
5319
5320     return {
5321         getStyle : function(el, name){
5322             return Roo.fly(el).getStyle(name);
5323         },
5324         /**
5325          * Compiles a selector/xpath query into a reusable function. The returned function
5326          * takes one parameter "root" (optional), which is the context node from where the query should start.
5327          * @param {String} selector The selector/xpath query
5328          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5329          * @return {Function}
5330          */
5331         compile : function(path, type){
5332             type = type || "select";
5333             
5334             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5335             var q = path, mode, lq;
5336             var tk = Roo.DomQuery.matchers;
5337             var tklen = tk.length;
5338             var mm;
5339
5340             // accept leading mode switch
5341             var lmode = q.match(modeRe);
5342             if(lmode && lmode[1]){
5343                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5344                 q = q.replace(lmode[1], "");
5345             }
5346             // strip leading slashes
5347             while(path.substr(0, 1)=="/"){
5348                 path = path.substr(1);
5349             }
5350
5351             while(q && lq != q){
5352                 lq = q;
5353                 var tm = q.match(tagTokenRe);
5354                 if(type == "select"){
5355                     if(tm){
5356                         if(tm[1] == "#"){
5357                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5358                         }else{
5359                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5360                         }
5361                         q = q.replace(tm[0], "");
5362                     }else if(q.substr(0, 1) != '@'){
5363                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5364                     }
5365                 }else{
5366                     if(tm){
5367                         if(tm[1] == "#"){
5368                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5369                         }else{
5370                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5371                         }
5372                         q = q.replace(tm[0], "");
5373                     }
5374                 }
5375                 while(!(mm = q.match(modeRe))){
5376                     var matched = false;
5377                     for(var j = 0; j < tklen; j++){
5378                         var t = tk[j];
5379                         var m = q.match(t.re);
5380                         if(m){
5381                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5382                                                     return m[i];
5383                                                 });
5384                             q = q.replace(m[0], "");
5385                             matched = true;
5386                             break;
5387                         }
5388                     }
5389                     // prevent infinite loop on bad selector
5390                     if(!matched){
5391                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5392                     }
5393                 }
5394                 if(mm[1]){
5395                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5396                     q = q.replace(mm[1], "");
5397                 }
5398             }
5399             fn[fn.length] = "return nodup(n);\n}";
5400             
5401              /** 
5402               * list of variables that need from compression as they are used by eval.
5403              *  eval:var:batch 
5404              *  eval:var:nodup
5405              *  eval:var:byTag
5406              *  eval:var:ById
5407              *  eval:var:getNodes
5408              *  eval:var:quickId
5409              *  eval:var:mode
5410              *  eval:var:root
5411              *  eval:var:n
5412              *  eval:var:byClassName
5413              *  eval:var:byPseudo
5414              *  eval:var:byAttribute
5415              *  eval:var:attrValue
5416              * 
5417              **/ 
5418             eval(fn.join(""));
5419             return f;
5420         },
5421
5422         /**
5423          * Selects a group of elements.
5424          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5425          * @param {Node} root (optional) The start of the query (defaults to document).
5426          * @return {Array}
5427          */
5428         select : function(path, root, type){
5429             if(!root || root == document){
5430                 root = document;
5431             }
5432             if(typeof root == "string"){
5433                 root = document.getElementById(root);
5434             }
5435             var paths = path.split(",");
5436             var results = [];
5437             for(var i = 0, len = paths.length; i < len; i++){
5438                 var p = paths[i].replace(trimRe, "");
5439                 if(!cache[p]){
5440                     cache[p] = Roo.DomQuery.compile(p);
5441                     if(!cache[p]){
5442                         throw p + " is not a valid selector";
5443                     }
5444                 }
5445                 var result = cache[p](root);
5446                 if(result && result != document){
5447                     results = results.concat(result);
5448                 }
5449             }
5450             if(paths.length > 1){
5451                 return nodup(results);
5452             }
5453             return results;
5454         },
5455
5456         /**
5457          * Selects a single element.
5458          * @param {String} selector The selector/xpath query
5459          * @param {Node} root (optional) The start of the query (defaults to document).
5460          * @return {Element}
5461          */
5462         selectNode : function(path, root){
5463             return Roo.DomQuery.select(path, root)[0];
5464         },
5465
5466         /**
5467          * Selects the value of a node, optionally replacing null with the defaultValue.
5468          * @param {String} selector The selector/xpath query
5469          * @param {Node} root (optional) The start of the query (defaults to document).
5470          * @param {String} defaultValue
5471          */
5472         selectValue : function(path, root, defaultValue){
5473             path = path.replace(trimRe, "");
5474             if(!valueCache[path]){
5475                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5476             }
5477             var n = valueCache[path](root);
5478             n = n[0] ? n[0] : n;
5479             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5480             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5481         },
5482
5483         /**
5484          * Selects the value of a node, parsing integers and floats.
5485          * @param {String} selector The selector/xpath query
5486          * @param {Node} root (optional) The start of the query (defaults to document).
5487          * @param {Number} defaultValue
5488          * @return {Number}
5489          */
5490         selectNumber : function(path, root, defaultValue){
5491             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5492             return parseFloat(v);
5493         },
5494
5495         /**
5496          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5497          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5498          * @param {String} selector The simple selector to test
5499          * @return {Boolean}
5500          */
5501         is : function(el, ss){
5502             if(typeof el == "string"){
5503                 el = document.getElementById(el);
5504             }
5505             var isArray = (el instanceof Array);
5506             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5507             return isArray ? (result.length == el.length) : (result.length > 0);
5508         },
5509
5510         /**
5511          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5512          * @param {Array} el An array of elements to filter
5513          * @param {String} selector The simple selector to test
5514          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5515          * the selector instead of the ones that match
5516          * @return {Array}
5517          */
5518         filter : function(els, ss, nonMatches){
5519             ss = ss.replace(trimRe, "");
5520             if(!simpleCache[ss]){
5521                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5522             }
5523             var result = simpleCache[ss](els);
5524             return nonMatches ? quickDiff(result, els) : result;
5525         },
5526
5527         /**
5528          * Collection of matching regular expressions and code snippets.
5529          */
5530         matchers : [{
5531                 re: /^\.([\w-]+)/,
5532                 select: 'n = byClassName(n, null, " {1} ");'
5533             }, {
5534                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5535                 select: 'n = byPseudo(n, "{1}", "{2}");'
5536             },{
5537                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5538                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5539             }, {
5540                 re: /^#([\w-]+)/,
5541                 select: 'n = byId(n, null, "{1}");'
5542             },{
5543                 re: /^@([\w-]+)/,
5544                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5545             }
5546         ],
5547
5548         /**
5549          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5550          * 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;.
5551          */
5552         operators : {
5553             "=" : function(a, v){
5554                 return a == v;
5555             },
5556             "!=" : function(a, v){
5557                 return a != v;
5558             },
5559             "^=" : function(a, v){
5560                 return a && a.substr(0, v.length) == v;
5561             },
5562             "$=" : function(a, v){
5563                 return a && a.substr(a.length-v.length) == v;
5564             },
5565             "*=" : function(a, v){
5566                 return a && a.indexOf(v) !== -1;
5567             },
5568             "%=" : function(a, v){
5569                 return (a % v) == 0;
5570             },
5571             "|=" : function(a, v){
5572                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5573             },
5574             "~=" : function(a, v){
5575                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5576             }
5577         },
5578
5579         /**
5580          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5581          * and the argument (if any) supplied in the selector.
5582          */
5583         pseudos : {
5584             "first-child" : function(c){
5585                 var r = [], ri = -1, n;
5586                 for(var i = 0, ci; ci = n = c[i]; i++){
5587                     while((n = n.previousSibling) && n.nodeType != 1);
5588                     if(!n){
5589                         r[++ri] = ci;
5590                     }
5591                 }
5592                 return r;
5593             },
5594
5595             "last-child" : function(c){
5596                 var r = [], ri = -1, n;
5597                 for(var i = 0, ci; ci = n = c[i]; i++){
5598                     while((n = n.nextSibling) && n.nodeType != 1);
5599                     if(!n){
5600                         r[++ri] = ci;
5601                     }
5602                 }
5603                 return r;
5604             },
5605
5606             "nth-child" : function(c, a) {
5607                 var r = [], ri = -1;
5608                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5609                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5610                 for(var i = 0, n; n = c[i]; i++){
5611                     var pn = n.parentNode;
5612                     if (batch != pn._batch) {
5613                         var j = 0;
5614                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5615                             if(cn.nodeType == 1){
5616                                cn.nodeIndex = ++j;
5617                             }
5618                         }
5619                         pn._batch = batch;
5620                     }
5621                     if (f == 1) {
5622                         if (l == 0 || n.nodeIndex == l){
5623                             r[++ri] = n;
5624                         }
5625                     } else if ((n.nodeIndex + l) % f == 0){
5626                         r[++ri] = n;
5627                     }
5628                 }
5629
5630                 return r;
5631             },
5632
5633             "only-child" : function(c){
5634                 var r = [], ri = -1;;
5635                 for(var i = 0, ci; ci = c[i]; i++){
5636                     if(!prev(ci) && !next(ci)){
5637                         r[++ri] = ci;
5638                     }
5639                 }
5640                 return r;
5641             },
5642
5643             "empty" : function(c){
5644                 var r = [], ri = -1;
5645                 for(var i = 0, ci; ci = c[i]; i++){
5646                     var cns = ci.childNodes, j = 0, cn, empty = true;
5647                     while(cn = cns[j]){
5648                         ++j;
5649                         if(cn.nodeType == 1 || cn.nodeType == 3){
5650                             empty = false;
5651                             break;
5652                         }
5653                     }
5654                     if(empty){
5655                         r[++ri] = ci;
5656                     }
5657                 }
5658                 return r;
5659             },
5660
5661             "contains" : function(c, v){
5662                 var r = [], ri = -1;
5663                 for(var i = 0, ci; ci = c[i]; i++){
5664                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5665                         r[++ri] = ci;
5666                     }
5667                 }
5668                 return r;
5669             },
5670
5671             "nodeValue" : function(c, v){
5672                 var r = [], ri = -1;
5673                 for(var i = 0, ci; ci = c[i]; i++){
5674                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5675                         r[++ri] = ci;
5676                     }
5677                 }
5678                 return r;
5679             },
5680
5681             "checked" : function(c){
5682                 var r = [], ri = -1;
5683                 for(var i = 0, ci; ci = c[i]; i++){
5684                     if(ci.checked == true){
5685                         r[++ri] = ci;
5686                     }
5687                 }
5688                 return r;
5689             },
5690
5691             "not" : function(c, ss){
5692                 return Roo.DomQuery.filter(c, ss, true);
5693             },
5694
5695             "odd" : function(c){
5696                 return this["nth-child"](c, "odd");
5697             },
5698
5699             "even" : function(c){
5700                 return this["nth-child"](c, "even");
5701             },
5702
5703             "nth" : function(c, a){
5704                 return c[a-1] || [];
5705             },
5706
5707             "first" : function(c){
5708                 return c[0] || [];
5709             },
5710
5711             "last" : function(c){
5712                 return c[c.length-1] || [];
5713             },
5714
5715             "has" : function(c, ss){
5716                 var s = Roo.DomQuery.select;
5717                 var r = [], ri = -1;
5718                 for(var i = 0, ci; ci = c[i]; i++){
5719                     if(s(ss, ci).length > 0){
5720                         r[++ri] = ci;
5721                     }
5722                 }
5723                 return r;
5724             },
5725
5726             "next" : function(c, ss){
5727                 var is = Roo.DomQuery.is;
5728                 var r = [], ri = -1;
5729                 for(var i = 0, ci; ci = c[i]; i++){
5730                     var n = next(ci);
5731                     if(n && is(n, ss)){
5732                         r[++ri] = ci;
5733                     }
5734                 }
5735                 return r;
5736             },
5737
5738             "prev" : function(c, ss){
5739                 var is = Roo.DomQuery.is;
5740                 var r = [], ri = -1;
5741                 for(var i = 0, ci; ci = c[i]; i++){
5742                     var n = prev(ci);
5743                     if(n && is(n, ss)){
5744                         r[++ri] = ci;
5745                     }
5746                 }
5747                 return r;
5748             }
5749         }
5750     };
5751 }();
5752
5753 /**
5754  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5755  * @param {String} path The selector/xpath query
5756  * @param {Node} root (optional) The start of the query (defaults to document).
5757  * @return {Array}
5758  * @member Roo
5759  * @method query
5760  */
5761 Roo.query = Roo.DomQuery.select;
5762 /*
5763  * Based on:
5764  * Ext JS Library 1.1.1
5765  * Copyright(c) 2006-2007, Ext JS, LLC.
5766  *
5767  * Originally Released Under LGPL - original licence link has changed is not relivant.
5768  *
5769  * Fork - LGPL
5770  * <script type="text/javascript">
5771  */
5772
5773 /**
5774  * @class Roo.util.Observable
5775  * Base class that provides a common interface for publishing events. Subclasses are expected to
5776  * to have a property "events" with all the events defined.<br>
5777  * For example:
5778  * <pre><code>
5779  Employee = function(name){
5780     this.name = name;
5781     this.addEvents({
5782         "fired" : true,
5783         "quit" : true
5784     });
5785  }
5786  Roo.extend(Employee, Roo.util.Observable);
5787 </code></pre>
5788  * @param {Object} config properties to use (incuding events / listeners)
5789  */
5790
5791 Roo.util.Observable = function(cfg){
5792     
5793     cfg = cfg|| {};
5794     this.addEvents(cfg.events || {});
5795     if (cfg.events) {
5796         delete cfg.events; // make sure
5797     }
5798      
5799     Roo.apply(this, cfg);
5800     
5801     if(this.listeners){
5802         this.on(this.listeners);
5803         delete this.listeners;
5804     }
5805 };
5806 Roo.util.Observable.prototype = {
5807     /** 
5808  * @cfg {Object} listeners  list of events and functions to call for this object, 
5809  * For example :
5810  * <pre><code>
5811     listeners :  { 
5812        'click' : function(e) {
5813            ..... 
5814         } ,
5815         .... 
5816     } 
5817   </code></pre>
5818  */
5819     
5820     
5821     /**
5822      * Fires the specified event with the passed parameters (minus the event name).
5823      * @param {String} eventName
5824      * @param {Object...} args Variable number of parameters are passed to handlers
5825      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5826      */
5827     fireEvent : function(){
5828         var ce = this.events[arguments[0].toLowerCase()];
5829         if(typeof ce == "object"){
5830             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5831         }else{
5832             return true;
5833         }
5834     },
5835
5836     // private
5837     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5838
5839     /**
5840      * Appends an event handler to this component
5841      * @param {String}   eventName The type of event to listen for
5842      * @param {Function} handler The method the event invokes
5843      * @param {Object}   scope (optional) The scope in which to execute the handler
5844      * function. The handler function's "this" context.
5845      * @param {Object}   options (optional) An object containing handler configuration
5846      * properties. This may contain any of the following properties:<ul>
5847      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5848      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5849      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5850      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5851      * by the specified number of milliseconds. If the event fires again within that time, the original
5852      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5853      * </ul><br>
5854      * <p>
5855      * <b>Combining Options</b><br>
5856      * Using the options argument, it is possible to combine different types of listeners:<br>
5857      * <br>
5858      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5859                 <pre><code>
5860                 el.on('click', this.onClick, this, {
5861                         single: true,
5862                 delay: 100,
5863                 forumId: 4
5864                 });
5865                 </code></pre>
5866      * <p>
5867      * <b>Attaching multiple handlers in 1 call</b><br>
5868      * The method also allows for a single argument to be passed which is a config object containing properties
5869      * which specify multiple handlers.
5870      * <pre><code>
5871                 el.on({
5872                         'click': {
5873                         fn: this.onClick,
5874                         scope: this,
5875                         delay: 100
5876                 }, 
5877                 'mouseover': {
5878                         fn: this.onMouseOver,
5879                         scope: this
5880                 },
5881                 'mouseout': {
5882                         fn: this.onMouseOut,
5883                         scope: this
5884                 }
5885                 });
5886                 </code></pre>
5887      * <p>
5888      * Or a shorthand syntax which passes the same scope object to all handlers:
5889         <pre><code>
5890                 el.on({
5891                         'click': this.onClick,
5892                 'mouseover': this.onMouseOver,
5893                 'mouseout': this.onMouseOut,
5894                 scope: this
5895                 });
5896                 </code></pre>
5897      */
5898     addListener : function(eventName, fn, scope, o){
5899         if(typeof eventName == "object"){
5900             o = eventName;
5901             for(var e in o){
5902                 if(this.filterOptRe.test(e)){
5903                     continue;
5904                 }
5905                 if(typeof o[e] == "function"){
5906                     // shared options
5907                     this.addListener(e, o[e], o.scope,  o);
5908                 }else{
5909                     // individual options
5910                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5911                 }
5912             }
5913             return;
5914         }
5915         o = (!o || typeof o == "boolean") ? {} : o;
5916         eventName = eventName.toLowerCase();
5917         var ce = this.events[eventName] || true;
5918         if(typeof ce == "boolean"){
5919             ce = new Roo.util.Event(this, eventName);
5920             this.events[eventName] = ce;
5921         }
5922         ce.addListener(fn, scope, o);
5923     },
5924
5925     /**
5926      * Removes a listener
5927      * @param {String}   eventName     The type of event to listen for
5928      * @param {Function} handler        The handler to remove
5929      * @param {Object}   scope  (optional) The scope (this object) for the handler
5930      */
5931     removeListener : function(eventName, fn, scope){
5932         var ce = this.events[eventName.toLowerCase()];
5933         if(typeof ce == "object"){
5934             ce.removeListener(fn, scope);
5935         }
5936     },
5937
5938     /**
5939      * Removes all listeners for this object
5940      */
5941     purgeListeners : function(){
5942         for(var evt in this.events){
5943             if(typeof this.events[evt] == "object"){
5944                  this.events[evt].clearListeners();
5945             }
5946         }
5947     },
5948
5949     relayEvents : function(o, events){
5950         var createHandler = function(ename){
5951             return function(){
5952                  
5953                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5954             };
5955         };
5956         for(var i = 0, len = events.length; i < len; i++){
5957             var ename = events[i];
5958             if(!this.events[ename]){
5959                 this.events[ename] = true;
5960             };
5961             o.on(ename, createHandler(ename), this);
5962         }
5963     },
5964
5965     /**
5966      * Used to define events on this Observable
5967      * @param {Object} object The object with the events defined
5968      */
5969     addEvents : function(o){
5970         if(!this.events){
5971             this.events = {};
5972         }
5973         Roo.applyIf(this.events, o);
5974     },
5975
5976     /**
5977      * Checks to see if this object has any listeners for a specified event
5978      * @param {String} eventName The name of the event to check for
5979      * @return {Boolean} True if the event is being listened for, else false
5980      */
5981     hasListener : function(eventName){
5982         var e = this.events[eventName];
5983         return typeof e == "object" && e.listeners.length > 0;
5984     }
5985 };
5986 /**
5987  * Appends an event handler to this element (shorthand for addListener)
5988  * @param {String}   eventName     The type of event to listen for
5989  * @param {Function} handler        The method the event invokes
5990  * @param {Object}   scope (optional) The scope in which to execute the handler
5991  * function. The handler function's "this" context.
5992  * @param {Object}   options  (optional)
5993  * @method
5994  */
5995 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5996 /**
5997  * Removes a listener (shorthand for removeListener)
5998  * @param {String}   eventName     The type of event to listen for
5999  * @param {Function} handler        The handler to remove
6000  * @param {Object}   scope  (optional) The scope (this object) for the handler
6001  * @method
6002  */
6003 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6004
6005 /**
6006  * Starts capture on the specified Observable. All events will be passed
6007  * to the supplied function with the event name + standard signature of the event
6008  * <b>before</b> the event is fired. If the supplied function returns false,
6009  * the event will not fire.
6010  * @param {Observable} o The Observable to capture
6011  * @param {Function} fn The function to call
6012  * @param {Object} scope (optional) The scope (this object) for the fn
6013  * @static
6014  */
6015 Roo.util.Observable.capture = function(o, fn, scope){
6016     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6017 };
6018
6019 /**
6020  * Removes <b>all</b> added captures from the Observable.
6021  * @param {Observable} o The Observable to release
6022  * @static
6023  */
6024 Roo.util.Observable.releaseCapture = function(o){
6025     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6026 };
6027
6028 (function(){
6029
6030     var createBuffered = function(h, o, scope){
6031         var task = new Roo.util.DelayedTask();
6032         return function(){
6033             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6034         };
6035     };
6036
6037     var createSingle = function(h, e, fn, scope){
6038         return function(){
6039             e.removeListener(fn, scope);
6040             return h.apply(scope, arguments);
6041         };
6042     };
6043
6044     var createDelayed = function(h, o, scope){
6045         return function(){
6046             var args = Array.prototype.slice.call(arguments, 0);
6047             setTimeout(function(){
6048                 h.apply(scope, args);
6049             }, o.delay || 10);
6050         };
6051     };
6052
6053     Roo.util.Event = function(obj, name){
6054         this.name = name;
6055         this.obj = obj;
6056         this.listeners = [];
6057     };
6058
6059     Roo.util.Event.prototype = {
6060         addListener : function(fn, scope, options){
6061             var o = options || {};
6062             scope = scope || this.obj;
6063             if(!this.isListening(fn, scope)){
6064                 var l = {fn: fn, scope: scope, options: o};
6065                 var h = fn;
6066                 if(o.delay){
6067                     h = createDelayed(h, o, scope);
6068                 }
6069                 if(o.single){
6070                     h = createSingle(h, this, fn, scope);
6071                 }
6072                 if(o.buffer){
6073                     h = createBuffered(h, o, scope);
6074                 }
6075                 l.fireFn = h;
6076                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6077                     this.listeners.push(l);
6078                 }else{
6079                     this.listeners = this.listeners.slice(0);
6080                     this.listeners.push(l);
6081                 }
6082             }
6083         },
6084
6085         findListener : function(fn, scope){
6086             scope = scope || this.obj;
6087             var ls = this.listeners;
6088             for(var i = 0, len = ls.length; i < len; i++){
6089                 var l = ls[i];
6090                 if(l.fn == fn && l.scope == scope){
6091                     return i;
6092                 }
6093             }
6094             return -1;
6095         },
6096
6097         isListening : function(fn, scope){
6098             return this.findListener(fn, scope) != -1;
6099         },
6100
6101         removeListener : function(fn, scope){
6102             var index;
6103             if((index = this.findListener(fn, scope)) != -1){
6104                 if(!this.firing){
6105                     this.listeners.splice(index, 1);
6106                 }else{
6107                     this.listeners = this.listeners.slice(0);
6108                     this.listeners.splice(index, 1);
6109                 }
6110                 return true;
6111             }
6112             return false;
6113         },
6114
6115         clearListeners : function(){
6116             this.listeners = [];
6117         },
6118
6119         fire : function(){
6120             var ls = this.listeners, scope, len = ls.length;
6121             if(len > 0){
6122                 this.firing = true;
6123                 var args = Array.prototype.slice.call(arguments, 0);                
6124                 for(var i = 0; i < len; i++){
6125                     var l = ls[i];
6126                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6127                         this.firing = false;
6128                         return false;
6129                     }
6130                 }
6131                 this.firing = false;
6132             }
6133             return true;
6134         }
6135     };
6136 })();/*
6137  * RooJS Library 
6138  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6139  *
6140  * Licence LGPL 
6141  *
6142  */
6143  
6144 /**
6145  * @class Roo.Document
6146  * @extends Roo.util.Observable
6147  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6148  * 
6149  * @param {Object} config the methods and properties of the 'base' class for the application.
6150  * 
6151  *  Generic Page handler - implement this to start your app..
6152  * 
6153  * eg.
6154  *  MyProject = new Roo.Document({
6155         events : {
6156             'load' : true // your events..
6157         },
6158         listeners : {
6159             'ready' : function() {
6160                 // fired on Roo.onReady()
6161             }
6162         }
6163  * 
6164  */
6165 Roo.Document = function(cfg) {
6166      
6167     this.addEvents({ 
6168         'ready' : true
6169     });
6170     Roo.util.Observable.call(this,cfg);
6171     
6172     var _this = this;
6173     
6174     Roo.onReady(function() {
6175         _this.fireEvent('ready');
6176     },null,false);
6177     
6178     
6179 }
6180
6181 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6182  * Based on:
6183  * Ext JS Library 1.1.1
6184  * Copyright(c) 2006-2007, Ext JS, LLC.
6185  *
6186  * Originally Released Under LGPL - original licence link has changed is not relivant.
6187  *
6188  * Fork - LGPL
6189  * <script type="text/javascript">
6190  */
6191
6192 /**
6193  * @class Roo.EventManager
6194  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6195  * several useful events directly.
6196  * See {@link Roo.EventObject} for more details on normalized event objects.
6197  * @singleton
6198  */
6199 Roo.EventManager = function(){
6200     var docReadyEvent, docReadyProcId, docReadyState = false;
6201     var resizeEvent, resizeTask, textEvent, textSize;
6202     var E = Roo.lib.Event;
6203     var D = Roo.lib.Dom;
6204
6205     
6206     
6207
6208     var fireDocReady = function(){
6209         if(!docReadyState){
6210             docReadyState = true;
6211             Roo.isReady = true;
6212             if(docReadyProcId){
6213                 clearInterval(docReadyProcId);
6214             }
6215             if(Roo.isGecko || Roo.isOpera) {
6216                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6217             }
6218             if(Roo.isIE){
6219                 var defer = document.getElementById("ie-deferred-loader");
6220                 if(defer){
6221                     defer.onreadystatechange = null;
6222                     defer.parentNode.removeChild(defer);
6223                 }
6224             }
6225             if(docReadyEvent){
6226                 docReadyEvent.fire();
6227                 docReadyEvent.clearListeners();
6228             }
6229         }
6230     };
6231     
6232     var initDocReady = function(){
6233         docReadyEvent = new Roo.util.Event();
6234         if(Roo.isGecko || Roo.isOpera) {
6235             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6236         }else if(Roo.isIE){
6237             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6238             var defer = document.getElementById("ie-deferred-loader");
6239             defer.onreadystatechange = function(){
6240                 if(this.readyState == "complete"){
6241                     fireDocReady();
6242                 }
6243             };
6244         }else if(Roo.isSafari){ 
6245             docReadyProcId = setInterval(function(){
6246                 var rs = document.readyState;
6247                 if(rs == "complete") {
6248                     fireDocReady();     
6249                  }
6250             }, 10);
6251         }
6252         // no matter what, make sure it fires on load
6253         E.on(window, "load", fireDocReady);
6254     };
6255
6256     var createBuffered = function(h, o){
6257         var task = new Roo.util.DelayedTask(h);
6258         return function(e){
6259             // create new event object impl so new events don't wipe out properties
6260             e = new Roo.EventObjectImpl(e);
6261             task.delay(o.buffer, h, null, [e]);
6262         };
6263     };
6264
6265     var createSingle = function(h, el, ename, fn){
6266         return function(e){
6267             Roo.EventManager.removeListener(el, ename, fn);
6268             h(e);
6269         };
6270     };
6271
6272     var createDelayed = function(h, o){
6273         return function(e){
6274             // create new event object impl so new events don't wipe out properties
6275             e = new Roo.EventObjectImpl(e);
6276             setTimeout(function(){
6277                 h(e);
6278             }, o.delay || 10);
6279         };
6280     };
6281     var transitionEndVal = false;
6282     
6283     var transitionEnd = function()
6284     {
6285         if (transitionEndVal) {
6286             return transitionEndVal;
6287         }
6288         var el = document.createElement('div');
6289
6290         var transEndEventNames = {
6291             WebkitTransition : 'webkitTransitionEnd',
6292             MozTransition    : 'transitionend',
6293             OTransition      : 'oTransitionEnd otransitionend',
6294             transition       : 'transitionend'
6295         };
6296     
6297         for (var name in transEndEventNames) {
6298             if (el.style[name] !== undefined) {
6299                 transitionEndVal = transEndEventNames[name];
6300                 return  transitionEndVal ;
6301             }
6302         }
6303     }
6304     
6305   
6306
6307     var listen = function(element, ename, opt, fn, scope){
6308         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6309         fn = fn || o.fn; scope = scope || o.scope;
6310         var el = Roo.getDom(element);
6311         
6312         
6313         if(!el){
6314             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6315         }
6316         
6317         if (ename == 'transitionend') {
6318             ename = transitionEnd();
6319         }
6320         var h = function(e){
6321             e = Roo.EventObject.setEvent(e);
6322             var t;
6323             if(o.delegate){
6324                 t = e.getTarget(o.delegate, el);
6325                 if(!t){
6326                     return;
6327                 }
6328             }else{
6329                 t = e.target;
6330             }
6331             if(o.stopEvent === true){
6332                 e.stopEvent();
6333             }
6334             if(o.preventDefault === true){
6335                e.preventDefault();
6336             }
6337             if(o.stopPropagation === true){
6338                 e.stopPropagation();
6339             }
6340
6341             if(o.normalized === false){
6342                 e = e.browserEvent;
6343             }
6344
6345             fn.call(scope || el, e, t, o);
6346         };
6347         if(o.delay){
6348             h = createDelayed(h, o);
6349         }
6350         if(o.single){
6351             h = createSingle(h, el, ename, fn);
6352         }
6353         if(o.buffer){
6354             h = createBuffered(h, o);
6355         }
6356         
6357         fn._handlers = fn._handlers || [];
6358         
6359         
6360         fn._handlers.push([Roo.id(el), ename, h]);
6361         
6362         
6363          
6364         E.on(el, ename, h);
6365         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6366             el.addEventListener("DOMMouseScroll", h, false);
6367             E.on(window, 'unload', function(){
6368                 el.removeEventListener("DOMMouseScroll", h, false);
6369             });
6370         }
6371         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6372             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6373         }
6374         return h;
6375     };
6376
6377     var stopListening = function(el, ename, fn){
6378         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6379         if(hds){
6380             for(var i = 0, len = hds.length; i < len; i++){
6381                 var h = hds[i];
6382                 if(h[0] == id && h[1] == ename){
6383                     hd = h[2];
6384                     hds.splice(i, 1);
6385                     break;
6386                 }
6387             }
6388         }
6389         E.un(el, ename, hd);
6390         el = Roo.getDom(el);
6391         if(ename == "mousewheel" && el.addEventListener){
6392             el.removeEventListener("DOMMouseScroll", hd, false);
6393         }
6394         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6395             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6396         }
6397     };
6398
6399     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6400     
6401     var pub = {
6402         
6403         
6404         /** 
6405          * Fix for doc tools
6406          * @scope Roo.EventManager
6407          */
6408         
6409         
6410         /** 
6411          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6412          * object with a Roo.EventObject
6413          * @param {Function} fn        The method the event invokes
6414          * @param {Object}   scope    An object that becomes the scope of the handler
6415          * @param {boolean}  override If true, the obj passed in becomes
6416          *                             the execution scope of the listener
6417          * @return {Function} The wrapped function
6418          * @deprecated
6419          */
6420         wrap : function(fn, scope, override){
6421             return function(e){
6422                 Roo.EventObject.setEvent(e);
6423                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6424             };
6425         },
6426         
6427         /**
6428      * Appends an event handler to an element (shorthand for addListener)
6429      * @param {String/HTMLElement}   element        The html element or id to assign the
6430      * @param {String}   eventName The type of event to listen for
6431      * @param {Function} handler The method the event invokes
6432      * @param {Object}   scope (optional) The scope in which to execute the handler
6433      * function. The handler function's "this" context.
6434      * @param {Object}   options (optional) An object containing handler configuration
6435      * properties. This may contain any of the following properties:<ul>
6436      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6437      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6438      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6439      * <li>preventDefault {Boolean} True to prevent the default action</li>
6440      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6441      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6442      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6443      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6444      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6445      * by the specified number of milliseconds. If the event fires again within that time, the original
6446      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6447      * </ul><br>
6448      * <p>
6449      * <b>Combining Options</b><br>
6450      * Using the options argument, it is possible to combine different types of listeners:<br>
6451      * <br>
6452      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6453      * Code:<pre><code>
6454 el.on('click', this.onClick, this, {
6455     single: true,
6456     delay: 100,
6457     stopEvent : true,
6458     forumId: 4
6459 });</code></pre>
6460      * <p>
6461      * <b>Attaching multiple handlers in 1 call</b><br>
6462       * The method also allows for a single argument to be passed which is a config object containing properties
6463      * which specify multiple handlers.
6464      * <p>
6465      * Code:<pre><code>
6466 el.on({
6467     'click' : {
6468         fn: this.onClick
6469         scope: this,
6470         delay: 100
6471     },
6472     'mouseover' : {
6473         fn: this.onMouseOver
6474         scope: this
6475     },
6476     'mouseout' : {
6477         fn: this.onMouseOut
6478         scope: this
6479     }
6480 });</code></pre>
6481      * <p>
6482      * Or a shorthand syntax:<br>
6483      * Code:<pre><code>
6484 el.on({
6485     'click' : this.onClick,
6486     'mouseover' : this.onMouseOver,
6487     'mouseout' : this.onMouseOut
6488     scope: this
6489 });</code></pre>
6490      */
6491         addListener : function(element, eventName, fn, scope, options){
6492             if(typeof eventName == "object"){
6493                 var o = eventName;
6494                 for(var e in o){
6495                     if(propRe.test(e)){
6496                         continue;
6497                     }
6498                     if(typeof o[e] == "function"){
6499                         // shared options
6500                         listen(element, e, o, o[e], o.scope);
6501                     }else{
6502                         // individual options
6503                         listen(element, e, o[e]);
6504                     }
6505                 }
6506                 return;
6507             }
6508             return listen(element, eventName, options, fn, scope);
6509         },
6510         
6511         /**
6512          * Removes an event handler
6513          *
6514          * @param {String/HTMLElement}   element        The id or html element to remove the 
6515          *                             event from
6516          * @param {String}   eventName     The type of event
6517          * @param {Function} fn
6518          * @return {Boolean} True if a listener was actually removed
6519          */
6520         removeListener : function(element, eventName, fn){
6521             return stopListening(element, eventName, fn);
6522         },
6523         
6524         /**
6525          * Fires when the document is ready (before onload and before images are loaded). Can be 
6526          * accessed shorthanded Roo.onReady().
6527          * @param {Function} fn        The method the event invokes
6528          * @param {Object}   scope    An  object that becomes the scope of the handler
6529          * @param {boolean}  options
6530          */
6531         onDocumentReady : function(fn, scope, options){
6532             if(docReadyState){ // if it already fired
6533                 docReadyEvent.addListener(fn, scope, options);
6534                 docReadyEvent.fire();
6535                 docReadyEvent.clearListeners();
6536                 return;
6537             }
6538             if(!docReadyEvent){
6539                 initDocReady();
6540             }
6541             docReadyEvent.addListener(fn, scope, options);
6542         },
6543         
6544         /**
6545          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6546          * @param {Function} fn        The method the event invokes
6547          * @param {Object}   scope    An object that becomes the scope of the handler
6548          * @param {boolean}  options
6549          */
6550         onWindowResize : function(fn, scope, options){
6551             if(!resizeEvent){
6552                 resizeEvent = new Roo.util.Event();
6553                 resizeTask = new Roo.util.DelayedTask(function(){
6554                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6555                 });
6556                 E.on(window, "resize", function(){
6557                     if(Roo.isIE){
6558                         resizeTask.delay(50);
6559                     }else{
6560                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6561                     }
6562                 });
6563             }
6564             resizeEvent.addListener(fn, scope, options);
6565         },
6566
6567         /**
6568          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6569          * @param {Function} fn        The method the event invokes
6570          * @param {Object}   scope    An object that becomes the scope of the handler
6571          * @param {boolean}  options
6572          */
6573         onTextResize : function(fn, scope, options){
6574             if(!textEvent){
6575                 textEvent = new Roo.util.Event();
6576                 var textEl = new Roo.Element(document.createElement('div'));
6577                 textEl.dom.className = 'x-text-resize';
6578                 textEl.dom.innerHTML = 'X';
6579                 textEl.appendTo(document.body);
6580                 textSize = textEl.dom.offsetHeight;
6581                 setInterval(function(){
6582                     if(textEl.dom.offsetHeight != textSize){
6583                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6584                     }
6585                 }, this.textResizeInterval);
6586             }
6587             textEvent.addListener(fn, scope, options);
6588         },
6589
6590         /**
6591          * Removes the passed window resize listener.
6592          * @param {Function} fn        The method the event invokes
6593          * @param {Object}   scope    The scope of handler
6594          */
6595         removeResizeListener : function(fn, scope){
6596             if(resizeEvent){
6597                 resizeEvent.removeListener(fn, scope);
6598             }
6599         },
6600
6601         // private
6602         fireResize : function(){
6603             if(resizeEvent){
6604                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6605             }   
6606         },
6607         /**
6608          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6609          */
6610         ieDeferSrc : false,
6611         /**
6612          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6613          */
6614         textResizeInterval : 50
6615     };
6616     
6617     /**
6618      * Fix for doc tools
6619      * @scopeAlias pub=Roo.EventManager
6620      */
6621     
6622      /**
6623      * Appends an event handler to an element (shorthand for addListener)
6624      * @param {String/HTMLElement}   element        The html element or id to assign the
6625      * @param {String}   eventName The type of event to listen for
6626      * @param {Function} handler The method the event invokes
6627      * @param {Object}   scope (optional) The scope in which to execute the handler
6628      * function. The handler function's "this" context.
6629      * @param {Object}   options (optional) An object containing handler configuration
6630      * properties. This may contain any of the following properties:<ul>
6631      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6632      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6633      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6634      * <li>preventDefault {Boolean} True to prevent the default action</li>
6635      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6636      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6637      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6638      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6639      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6640      * by the specified number of milliseconds. If the event fires again within that time, the original
6641      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6642      * </ul><br>
6643      * <p>
6644      * <b>Combining Options</b><br>
6645      * Using the options argument, it is possible to combine different types of listeners:<br>
6646      * <br>
6647      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6648      * Code:<pre><code>
6649 el.on('click', this.onClick, this, {
6650     single: true,
6651     delay: 100,
6652     stopEvent : true,
6653     forumId: 4
6654 });</code></pre>
6655      * <p>
6656      * <b>Attaching multiple handlers in 1 call</b><br>
6657       * The method also allows for a single argument to be passed which is a config object containing properties
6658      * which specify multiple handlers.
6659      * <p>
6660      * Code:<pre><code>
6661 el.on({
6662     'click' : {
6663         fn: this.onClick
6664         scope: this,
6665         delay: 100
6666     },
6667     'mouseover' : {
6668         fn: this.onMouseOver
6669         scope: this
6670     },
6671     'mouseout' : {
6672         fn: this.onMouseOut
6673         scope: this
6674     }
6675 });</code></pre>
6676      * <p>
6677      * Or a shorthand syntax:<br>
6678      * Code:<pre><code>
6679 el.on({
6680     'click' : this.onClick,
6681     'mouseover' : this.onMouseOver,
6682     'mouseout' : this.onMouseOut
6683     scope: this
6684 });</code></pre>
6685      */
6686     pub.on = pub.addListener;
6687     pub.un = pub.removeListener;
6688
6689     pub.stoppedMouseDownEvent = new Roo.util.Event();
6690     return pub;
6691 }();
6692 /**
6693   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6694   * @param {Function} fn        The method the event invokes
6695   * @param {Object}   scope    An  object that becomes the scope of the handler
6696   * @param {boolean}  override If true, the obj passed in becomes
6697   *                             the execution scope of the listener
6698   * @member Roo
6699   * @method onReady
6700  */
6701 Roo.onReady = Roo.EventManager.onDocumentReady;
6702
6703 Roo.onReady(function(){
6704     var bd = Roo.get(document.body);
6705     if(!bd){ return; }
6706
6707     var cls = [
6708             Roo.isIE ? "roo-ie"
6709             : Roo.isIE11 ? "roo-ie11"
6710             : Roo.isEdge ? "roo-edge"
6711             : Roo.isGecko ? "roo-gecko"
6712             : Roo.isOpera ? "roo-opera"
6713             : Roo.isSafari ? "roo-safari" : ""];
6714
6715     if(Roo.isMac){
6716         cls.push("roo-mac");
6717     }
6718     if(Roo.isLinux){
6719         cls.push("roo-linux");
6720     }
6721     if(Roo.isIOS){
6722         cls.push("roo-ios");
6723     }
6724     if(Roo.isTouch){
6725         cls.push("roo-touch");
6726     }
6727     if(Roo.isBorderBox){
6728         cls.push('roo-border-box');
6729     }
6730     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6731         var p = bd.dom.parentNode;
6732         if(p){
6733             p.className += ' roo-strict';
6734         }
6735     }
6736     bd.addClass(cls.join(' '));
6737 });
6738
6739 /**
6740  * @class Roo.EventObject
6741  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6742  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6743  * Example:
6744  * <pre><code>
6745  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6746     e.preventDefault();
6747     var target = e.getTarget();
6748     ...
6749  }
6750  var myDiv = Roo.get("myDiv");
6751  myDiv.on("click", handleClick);
6752  //or
6753  Roo.EventManager.on("myDiv", 'click', handleClick);
6754  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6755  </code></pre>
6756  * @singleton
6757  */
6758 Roo.EventObject = function(){
6759     
6760     var E = Roo.lib.Event;
6761     
6762     // safari keypress events for special keys return bad keycodes
6763     var safariKeys = {
6764         63234 : 37, // left
6765         63235 : 39, // right
6766         63232 : 38, // up
6767         63233 : 40, // down
6768         63276 : 33, // page up
6769         63277 : 34, // page down
6770         63272 : 46, // delete
6771         63273 : 36, // home
6772         63275 : 35  // end
6773     };
6774
6775     // normalize button clicks
6776     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6777                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6778
6779     Roo.EventObjectImpl = function(e){
6780         if(e){
6781             this.setEvent(e.browserEvent || e);
6782         }
6783     };
6784     Roo.EventObjectImpl.prototype = {
6785         /**
6786          * Used to fix doc tools.
6787          * @scope Roo.EventObject.prototype
6788          */
6789             
6790
6791         
6792         
6793         /** The normal browser event */
6794         browserEvent : null,
6795         /** The button pressed in a mouse event */
6796         button : -1,
6797         /** True if the shift key was down during the event */
6798         shiftKey : false,
6799         /** True if the control key was down during the event */
6800         ctrlKey : false,
6801         /** True if the alt key was down during the event */
6802         altKey : false,
6803
6804         /** Key constant 
6805         * @type Number */
6806         BACKSPACE : 8,
6807         /** Key constant 
6808         * @type Number */
6809         TAB : 9,
6810         /** Key constant 
6811         * @type Number */
6812         RETURN : 13,
6813         /** Key constant 
6814         * @type Number */
6815         ENTER : 13,
6816         /** Key constant 
6817         * @type Number */
6818         SHIFT : 16,
6819         /** Key constant 
6820         * @type Number */
6821         CONTROL : 17,
6822         /** Key constant 
6823         * @type Number */
6824         ESC : 27,
6825         /** Key constant 
6826         * @type Number */
6827         SPACE : 32,
6828         /** Key constant 
6829         * @type Number */
6830         PAGEUP : 33,
6831         /** Key constant 
6832         * @type Number */
6833         PAGEDOWN : 34,
6834         /** Key constant 
6835         * @type Number */
6836         END : 35,
6837         /** Key constant 
6838         * @type Number */
6839         HOME : 36,
6840         /** Key constant 
6841         * @type Number */
6842         LEFT : 37,
6843         /** Key constant 
6844         * @type Number */
6845         UP : 38,
6846         /** Key constant 
6847         * @type Number */
6848         RIGHT : 39,
6849         /** Key constant 
6850         * @type Number */
6851         DOWN : 40,
6852         /** Key constant 
6853         * @type Number */
6854         DELETE : 46,
6855         /** Key constant 
6856         * @type Number */
6857         F5 : 116,
6858
6859            /** @private */
6860         setEvent : function(e){
6861             if(e == this || (e && e.browserEvent)){ // already wrapped
6862                 return e;
6863             }
6864             this.browserEvent = e;
6865             if(e){
6866                 // normalize buttons
6867                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6868                 if(e.type == 'click' && this.button == -1){
6869                     this.button = 0;
6870                 }
6871                 this.type = e.type;
6872                 this.shiftKey = e.shiftKey;
6873                 // mac metaKey behaves like ctrlKey
6874                 this.ctrlKey = e.ctrlKey || e.metaKey;
6875                 this.altKey = e.altKey;
6876                 // in getKey these will be normalized for the mac
6877                 this.keyCode = e.keyCode;
6878                 // keyup warnings on firefox.
6879                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6880                 // cache the target for the delayed and or buffered events
6881                 this.target = E.getTarget(e);
6882                 // same for XY
6883                 this.xy = E.getXY(e);
6884             }else{
6885                 this.button = -1;
6886                 this.shiftKey = false;
6887                 this.ctrlKey = false;
6888                 this.altKey = false;
6889                 this.keyCode = 0;
6890                 this.charCode =0;
6891                 this.target = null;
6892                 this.xy = [0, 0];
6893             }
6894             return this;
6895         },
6896
6897         /**
6898          * Stop the event (preventDefault and stopPropagation)
6899          */
6900         stopEvent : function(){
6901             if(this.browserEvent){
6902                 if(this.browserEvent.type == 'mousedown'){
6903                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6904                 }
6905                 E.stopEvent(this.browserEvent);
6906             }
6907         },
6908
6909         /**
6910          * Prevents the browsers default handling of the event.
6911          */
6912         preventDefault : function(){
6913             if(this.browserEvent){
6914                 E.preventDefault(this.browserEvent);
6915             }
6916         },
6917
6918         /** @private */
6919         isNavKeyPress : function(){
6920             var k = this.keyCode;
6921             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6922             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6923         },
6924
6925         isSpecialKey : function(){
6926             var k = this.keyCode;
6927             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6928             (k == 16) || (k == 17) ||
6929             (k >= 18 && k <= 20) ||
6930             (k >= 33 && k <= 35) ||
6931             (k >= 36 && k <= 39) ||
6932             (k >= 44 && k <= 45);
6933         },
6934         /**
6935          * Cancels bubbling of the event.
6936          */
6937         stopPropagation : function(){
6938             if(this.browserEvent){
6939                 if(this.type == 'mousedown'){
6940                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6941                 }
6942                 E.stopPropagation(this.browserEvent);
6943             }
6944         },
6945
6946         /**
6947          * Gets the key code for the event.
6948          * @return {Number}
6949          */
6950         getCharCode : function(){
6951             return this.charCode || this.keyCode;
6952         },
6953
6954         /**
6955          * Returns a normalized keyCode for the event.
6956          * @return {Number} The key code
6957          */
6958         getKey : function(){
6959             var k = this.keyCode || this.charCode;
6960             return Roo.isSafari ? (safariKeys[k] || k) : k;
6961         },
6962
6963         /**
6964          * Gets the x coordinate of the event.
6965          * @return {Number}
6966          */
6967         getPageX : function(){
6968             return this.xy[0];
6969         },
6970
6971         /**
6972          * Gets the y coordinate of the event.
6973          * @return {Number}
6974          */
6975         getPageY : function(){
6976             return this.xy[1];
6977         },
6978
6979         /**
6980          * Gets the time of the event.
6981          * @return {Number}
6982          */
6983         getTime : function(){
6984             if(this.browserEvent){
6985                 return E.getTime(this.browserEvent);
6986             }
6987             return null;
6988         },
6989
6990         /**
6991          * Gets the page coordinates of the event.
6992          * @return {Array} The xy values like [x, y]
6993          */
6994         getXY : function(){
6995             return this.xy;
6996         },
6997
6998         /**
6999          * Gets the target for the event.
7000          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7001          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7002                 search as a number or element (defaults to 10 || document.body)
7003          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7004          * @return {HTMLelement}
7005          */
7006         getTarget : function(selector, maxDepth, returnEl){
7007             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7008         },
7009         /**
7010          * Gets the related target.
7011          * @return {HTMLElement}
7012          */
7013         getRelatedTarget : function(){
7014             if(this.browserEvent){
7015                 return E.getRelatedTarget(this.browserEvent);
7016             }
7017             return null;
7018         },
7019
7020         /**
7021          * Normalizes mouse wheel delta across browsers
7022          * @return {Number} The delta
7023          */
7024         getWheelDelta : function(){
7025             var e = this.browserEvent;
7026             var delta = 0;
7027             if(e.wheelDelta){ /* IE/Opera. */
7028                 delta = e.wheelDelta/120;
7029             }else if(e.detail){ /* Mozilla case. */
7030                 delta = -e.detail/3;
7031             }
7032             return delta;
7033         },
7034
7035         /**
7036          * Returns true if the control, meta, shift or alt key was pressed during this event.
7037          * @return {Boolean}
7038          */
7039         hasModifier : function(){
7040             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7041         },
7042
7043         /**
7044          * Returns true if the target of this event equals el or is a child of el
7045          * @param {String/HTMLElement/Element} el
7046          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7047          * @return {Boolean}
7048          */
7049         within : function(el, related){
7050             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7051             return t && Roo.fly(el).contains(t);
7052         },
7053
7054         getPoint : function(){
7055             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7056         }
7057     };
7058
7059     return new Roo.EventObjectImpl();
7060 }();
7061             
7062     /*
7063  * Based on:
7064  * Ext JS Library 1.1.1
7065  * Copyright(c) 2006-2007, Ext JS, LLC.
7066  *
7067  * Originally Released Under LGPL - original licence link has changed is not relivant.
7068  *
7069  * Fork - LGPL
7070  * <script type="text/javascript">
7071  */
7072
7073  
7074 // was in Composite Element!??!?!
7075  
7076 (function(){
7077     var D = Roo.lib.Dom;
7078     var E = Roo.lib.Event;
7079     var A = Roo.lib.Anim;
7080
7081     // local style camelizing for speed
7082     var propCache = {};
7083     var camelRe = /(-[a-z])/gi;
7084     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7085     var view = document.defaultView;
7086
7087 /**
7088  * @class Roo.Element
7089  * Represents an Element in the DOM.<br><br>
7090  * Usage:<br>
7091 <pre><code>
7092 var el = Roo.get("my-div");
7093
7094 // or with getEl
7095 var el = getEl("my-div");
7096
7097 // or with a DOM element
7098 var el = Roo.get(myDivElement);
7099 </code></pre>
7100  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7101  * each call instead of constructing a new one.<br><br>
7102  * <b>Animations</b><br />
7103  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7104  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7105 <pre>
7106 Option    Default   Description
7107 --------- --------  ---------------------------------------------
7108 duration  .35       The duration of the animation in seconds
7109 easing    easeOut   The YUI easing method
7110 callback  none      A function to execute when the anim completes
7111 scope     this      The scope (this) of the callback function
7112 </pre>
7113 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7114 * manipulate the animation. Here's an example:
7115 <pre><code>
7116 var el = Roo.get("my-div");
7117
7118 // no animation
7119 el.setWidth(100);
7120
7121 // default animation
7122 el.setWidth(100, true);
7123
7124 // animation with some options set
7125 el.setWidth(100, {
7126     duration: 1,
7127     callback: this.foo,
7128     scope: this
7129 });
7130
7131 // using the "anim" property to get the Anim object
7132 var opt = {
7133     duration: 1,
7134     callback: this.foo,
7135     scope: this
7136 };
7137 el.setWidth(100, opt);
7138 ...
7139 if(opt.anim.isAnimated()){
7140     opt.anim.stop();
7141 }
7142 </code></pre>
7143 * <b> Composite (Collections of) Elements</b><br />
7144  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7145  * @constructor Create a new Element directly.
7146  * @param {String/HTMLElement} element
7147  * @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).
7148  */
7149     Roo.Element = function(element, forceNew){
7150         var dom = typeof element == "string" ?
7151                 document.getElementById(element) : element;
7152         if(!dom){ // invalid id/element
7153             return null;
7154         }
7155         var id = dom.id;
7156         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7157             return Roo.Element.cache[id];
7158         }
7159
7160         /**
7161          * The DOM element
7162          * @type HTMLElement
7163          */
7164         this.dom = dom;
7165
7166         /**
7167          * The DOM element ID
7168          * @type String
7169          */
7170         this.id = id || Roo.id(dom);
7171     };
7172
7173     var El = Roo.Element;
7174
7175     El.prototype = {
7176         /**
7177          * The element's default display mode  (defaults to "") 
7178          * @type String
7179          */
7180         originalDisplay : "",
7181
7182         
7183         // note this is overridden in BS version..
7184         visibilityMode : 1, 
7185         /**
7186          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7187          * @type String
7188          */
7189         defaultUnit : "px",
7190         
7191         /**
7192          * Sets the element's visibility mode. When setVisible() is called it
7193          * will use this to determine whether to set the visibility or the display property.
7194          * @param visMode Element.VISIBILITY or Element.DISPLAY
7195          * @return {Roo.Element} this
7196          */
7197         setVisibilityMode : function(visMode){
7198             this.visibilityMode = visMode;
7199             return this;
7200         },
7201         /**
7202          * Convenience method for setVisibilityMode(Element.DISPLAY)
7203          * @param {String} display (optional) What to set display to when visible
7204          * @return {Roo.Element} this
7205          */
7206         enableDisplayMode : function(display){
7207             this.setVisibilityMode(El.DISPLAY);
7208             if(typeof display != "undefined") { this.originalDisplay = display; }
7209             return this;
7210         },
7211
7212         /**
7213          * 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)
7214          * @param {String} selector The simple selector to test
7215          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7216                 search as a number or element (defaults to 10 || document.body)
7217          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7218          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7219          */
7220         findParent : function(simpleSelector, maxDepth, returnEl){
7221             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7222             maxDepth = maxDepth || 50;
7223             if(typeof maxDepth != "number"){
7224                 stopEl = Roo.getDom(maxDepth);
7225                 maxDepth = 10;
7226             }
7227             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7228                 if(dq.is(p, simpleSelector)){
7229                     return returnEl ? Roo.get(p) : p;
7230                 }
7231                 depth++;
7232                 p = p.parentNode;
7233             }
7234             return null;
7235         },
7236
7237
7238         /**
7239          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7240          * @param {String} selector The simple selector to test
7241          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7242                 search as a number or element (defaults to 10 || document.body)
7243          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7244          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7245          */
7246         findParentNode : function(simpleSelector, maxDepth, returnEl){
7247             var p = Roo.fly(this.dom.parentNode, '_internal');
7248             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7249         },
7250         
7251         /**
7252          * Looks at  the scrollable parent element
7253          */
7254         findScrollableParent : function()
7255         {
7256             var overflowRegex = /(auto|scroll)/;
7257             
7258             if(this.getStyle('position') === 'fixed'){
7259                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7260             }
7261             
7262             var excludeStaticParent = this.getStyle('position') === "absolute";
7263             
7264             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7265                 
7266                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7267                     continue;
7268                 }
7269                 
7270                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7271                     return parent;
7272                 }
7273                 
7274                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7275                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7276                 }
7277             }
7278             
7279             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7280         },
7281
7282         /**
7283          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7284          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7285          * @param {String} selector The simple selector to test
7286          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7287                 search as a number or element (defaults to 10 || document.body)
7288          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7289          */
7290         up : function(simpleSelector, maxDepth){
7291             return this.findParentNode(simpleSelector, maxDepth, true);
7292         },
7293
7294
7295
7296         /**
7297          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7298          * @param {String} selector The simple selector to test
7299          * @return {Boolean} True if this element matches the selector, else false
7300          */
7301         is : function(simpleSelector){
7302             return Roo.DomQuery.is(this.dom, simpleSelector);
7303         },
7304
7305         /**
7306          * Perform animation on this element.
7307          * @param {Object} args The YUI animation control args
7308          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7309          * @param {Function} onComplete (optional) Function to call when animation completes
7310          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7311          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7312          * @return {Roo.Element} this
7313          */
7314         animate : function(args, duration, onComplete, easing, animType){
7315             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7316             return this;
7317         },
7318
7319         /*
7320          * @private Internal animation call
7321          */
7322         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7323             animType = animType || 'run';
7324             opt = opt || {};
7325             var anim = Roo.lib.Anim[animType](
7326                 this.dom, args,
7327                 (opt.duration || defaultDur) || .35,
7328                 (opt.easing || defaultEase) || 'easeOut',
7329                 function(){
7330                     Roo.callback(cb, this);
7331                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7332                 },
7333                 this
7334             );
7335             opt.anim = anim;
7336             return anim;
7337         },
7338
7339         // private legacy anim prep
7340         preanim : function(a, i){
7341             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7342         },
7343
7344         /**
7345          * Removes worthless text nodes
7346          * @param {Boolean} forceReclean (optional) By default the element
7347          * keeps track if it has been cleaned already so
7348          * you can call this over and over. However, if you update the element and
7349          * need to force a reclean, you can pass true.
7350          */
7351         clean : function(forceReclean){
7352             if(this.isCleaned && forceReclean !== true){
7353                 return this;
7354             }
7355             var ns = /\S/;
7356             var d = this.dom, n = d.firstChild, ni = -1;
7357             while(n){
7358                 var nx = n.nextSibling;
7359                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7360                     d.removeChild(n);
7361                 }else{
7362                     n.nodeIndex = ++ni;
7363                 }
7364                 n = nx;
7365             }
7366             this.isCleaned = true;
7367             return this;
7368         },
7369
7370         // private
7371         calcOffsetsTo : function(el){
7372             el = Roo.get(el);
7373             var d = el.dom;
7374             var restorePos = false;
7375             if(el.getStyle('position') == 'static'){
7376                 el.position('relative');
7377                 restorePos = true;
7378             }
7379             var x = 0, y =0;
7380             var op = this.dom;
7381             while(op && op != d && op.tagName != 'HTML'){
7382                 x+= op.offsetLeft;
7383                 y+= op.offsetTop;
7384                 op = op.offsetParent;
7385             }
7386             if(restorePos){
7387                 el.position('static');
7388             }
7389             return [x, y];
7390         },
7391
7392         /**
7393          * Scrolls this element into view within the passed container.
7394          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7395          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7396          * @return {Roo.Element} this
7397          */
7398         scrollIntoView : function(container, hscroll){
7399             var c = Roo.getDom(container) || document.body;
7400             var el = this.dom;
7401
7402             var o = this.calcOffsetsTo(c),
7403                 l = o[0],
7404                 t = o[1],
7405                 b = t+el.offsetHeight,
7406                 r = l+el.offsetWidth;
7407
7408             var ch = c.clientHeight;
7409             var ct = parseInt(c.scrollTop, 10);
7410             var cl = parseInt(c.scrollLeft, 10);
7411             var cb = ct + ch;
7412             var cr = cl + c.clientWidth;
7413
7414             if(t < ct){
7415                 c.scrollTop = t;
7416             }else if(b > cb){
7417                 c.scrollTop = b-ch;
7418             }
7419
7420             if(hscroll !== false){
7421                 if(l < cl){
7422                     c.scrollLeft = l;
7423                 }else if(r > cr){
7424                     c.scrollLeft = r-c.clientWidth;
7425                 }
7426             }
7427             return this;
7428         },
7429
7430         // private
7431         scrollChildIntoView : function(child, hscroll){
7432             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7433         },
7434
7435         /**
7436          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7437          * the new height may not be available immediately.
7438          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7439          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7440          * @param {Function} onComplete (optional) Function to call when animation completes
7441          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7442          * @return {Roo.Element} this
7443          */
7444         autoHeight : function(animate, duration, onComplete, easing){
7445             var oldHeight = this.getHeight();
7446             this.clip();
7447             this.setHeight(1); // force clipping
7448             setTimeout(function(){
7449                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7450                 if(!animate){
7451                     this.setHeight(height);
7452                     this.unclip();
7453                     if(typeof onComplete == "function"){
7454                         onComplete();
7455                     }
7456                 }else{
7457                     this.setHeight(oldHeight); // restore original height
7458                     this.setHeight(height, animate, duration, function(){
7459                         this.unclip();
7460                         if(typeof onComplete == "function") { onComplete(); }
7461                     }.createDelegate(this), easing);
7462                 }
7463             }.createDelegate(this), 0);
7464             return this;
7465         },
7466
7467         /**
7468          * Returns true if this element is an ancestor of the passed element
7469          * @param {HTMLElement/String} el The element to check
7470          * @return {Boolean} True if this element is an ancestor of el, else false
7471          */
7472         contains : function(el){
7473             if(!el){return false;}
7474             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7475         },
7476
7477         /**
7478          * Checks whether the element is currently visible using both visibility and display properties.
7479          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7480          * @return {Boolean} True if the element is currently visible, else false
7481          */
7482         isVisible : function(deep) {
7483             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7484             if(deep !== true || !vis){
7485                 return vis;
7486             }
7487             var p = this.dom.parentNode;
7488             while(p && p.tagName.toLowerCase() != "body"){
7489                 if(!Roo.fly(p, '_isVisible').isVisible()){
7490                     return false;
7491                 }
7492                 p = p.parentNode;
7493             }
7494             return true;
7495         },
7496
7497         /**
7498          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7499          * @param {String} selector The CSS selector
7500          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7501          * @return {CompositeElement/CompositeElementLite} The composite element
7502          */
7503         select : function(selector, unique){
7504             return El.select(selector, unique, this.dom);
7505         },
7506
7507         /**
7508          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7509          * @param {String} selector The CSS selector
7510          * @return {Array} An array of the matched nodes
7511          */
7512         query : function(selector, unique){
7513             return Roo.DomQuery.select(selector, this.dom);
7514         },
7515
7516         /**
7517          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7518          * @param {String} selector The CSS selector
7519          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7520          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7521          */
7522         child : function(selector, returnDom){
7523             var n = Roo.DomQuery.selectNode(selector, this.dom);
7524             return returnDom ? n : Roo.get(n);
7525         },
7526
7527         /**
7528          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7529          * @param {String} selector The CSS selector
7530          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7531          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7532          */
7533         down : function(selector, returnDom){
7534             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7535             return returnDom ? n : Roo.get(n);
7536         },
7537
7538         /**
7539          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7540          * @param {String} group The group the DD object is member of
7541          * @param {Object} config The DD config object
7542          * @param {Object} overrides An object containing methods to override/implement on the DD object
7543          * @return {Roo.dd.DD} The DD object
7544          */
7545         initDD : function(group, config, overrides){
7546             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7547             return Roo.apply(dd, overrides);
7548         },
7549
7550         /**
7551          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7552          * @param {String} group The group the DDProxy object is member of
7553          * @param {Object} config The DDProxy config object
7554          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7555          * @return {Roo.dd.DDProxy} The DDProxy object
7556          */
7557         initDDProxy : function(group, config, overrides){
7558             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7559             return Roo.apply(dd, overrides);
7560         },
7561
7562         /**
7563          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7564          * @param {String} group The group the DDTarget object is member of
7565          * @param {Object} config The DDTarget config object
7566          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7567          * @return {Roo.dd.DDTarget} The DDTarget object
7568          */
7569         initDDTarget : function(group, config, overrides){
7570             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7571             return Roo.apply(dd, overrides);
7572         },
7573
7574         /**
7575          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7576          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7577          * @param {Boolean} visible Whether the element is visible
7578          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7579          * @return {Roo.Element} this
7580          */
7581          setVisible : function(visible, animate){
7582             if(!animate || !A){
7583                 if(this.visibilityMode == El.DISPLAY){
7584                     this.setDisplayed(visible);
7585                 }else{
7586                     this.fixDisplay();
7587                     this.dom.style.visibility = visible ? "visible" : "hidden";
7588                 }
7589             }else{
7590                 // closure for composites
7591                 var dom = this.dom;
7592                 var visMode = this.visibilityMode;
7593                 if(visible){
7594                     this.setOpacity(.01);
7595                     this.setVisible(true);
7596                 }
7597                 this.anim({opacity: { to: (visible?1:0) }},
7598                       this.preanim(arguments, 1),
7599                       null, .35, 'easeIn', function(){
7600                          if(!visible){
7601                              if(visMode == El.DISPLAY){
7602                                  dom.style.display = "none";
7603                              }else{
7604                                  dom.style.visibility = "hidden";
7605                              }
7606                              Roo.get(dom).setOpacity(1);
7607                          }
7608                      });
7609             }
7610             return this;
7611         },
7612
7613         /**
7614          * Returns true if display is not "none"
7615          * @return {Boolean}
7616          */
7617         isDisplayed : function() {
7618             return this.getStyle("display") != "none";
7619         },
7620
7621         /**
7622          * Toggles the element's visibility or display, depending on visibility mode.
7623          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7624          * @return {Roo.Element} this
7625          */
7626         toggle : function(animate){
7627             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7628             return this;
7629         },
7630
7631         /**
7632          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7633          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7634          * @return {Roo.Element} this
7635          */
7636         setDisplayed : function(value) {
7637             if(typeof value == "boolean"){
7638                value = value ? this.originalDisplay : "none";
7639             }
7640             this.setStyle("display", value);
7641             return this;
7642         },
7643
7644         /**
7645          * Tries to focus the element. Any exceptions are caught and ignored.
7646          * @return {Roo.Element} this
7647          */
7648         focus : function() {
7649             try{
7650                 this.dom.focus();
7651             }catch(e){}
7652             return this;
7653         },
7654
7655         /**
7656          * Tries to blur the element. Any exceptions are caught and ignored.
7657          * @return {Roo.Element} this
7658          */
7659         blur : function() {
7660             try{
7661                 this.dom.blur();
7662             }catch(e){}
7663             return this;
7664         },
7665
7666         /**
7667          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7668          * @param {String/Array} className The CSS class to add, or an array of classes
7669          * @return {Roo.Element} this
7670          */
7671         addClass : function(className){
7672             if(className instanceof Array){
7673                 for(var i = 0, len = className.length; i < len; i++) {
7674                     this.addClass(className[i]);
7675                 }
7676             }else{
7677                 if(className && !this.hasClass(className)){
7678                     this.dom.className = this.dom.className + " " + className;
7679                 }
7680             }
7681             return this;
7682         },
7683
7684         /**
7685          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7686          * @param {String/Array} className The CSS class to add, or an array of classes
7687          * @return {Roo.Element} this
7688          */
7689         radioClass : function(className){
7690             var siblings = this.dom.parentNode.childNodes;
7691             for(var i = 0; i < siblings.length; i++) {
7692                 var s = siblings[i];
7693                 if(s.nodeType == 1){
7694                     Roo.get(s).removeClass(className);
7695                 }
7696             }
7697             this.addClass(className);
7698             return this;
7699         },
7700
7701         /**
7702          * Removes one or more CSS classes from the element.
7703          * @param {String/Array} className The CSS class to remove, or an array of classes
7704          * @return {Roo.Element} this
7705          */
7706         removeClass : function(className){
7707             if(!className || !this.dom.className){
7708                 return this;
7709             }
7710             if(className instanceof Array){
7711                 for(var i = 0, len = className.length; i < len; i++) {
7712                     this.removeClass(className[i]);
7713                 }
7714             }else{
7715                 if(this.hasClass(className)){
7716                     var re = this.classReCache[className];
7717                     if (!re) {
7718                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7719                        this.classReCache[className] = re;
7720                     }
7721                     this.dom.className =
7722                         this.dom.className.replace(re, " ");
7723                 }
7724             }
7725             return this;
7726         },
7727
7728         // private
7729         classReCache: {},
7730
7731         /**
7732          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7733          * @param {String} className The CSS class to toggle
7734          * @return {Roo.Element} this
7735          */
7736         toggleClass : function(className){
7737             if(this.hasClass(className)){
7738                 this.removeClass(className);
7739             }else{
7740                 this.addClass(className);
7741             }
7742             return this;
7743         },
7744
7745         /**
7746          * Checks if the specified CSS class exists on this element's DOM node.
7747          * @param {String} className The CSS class to check for
7748          * @return {Boolean} True if the class exists, else false
7749          */
7750         hasClass : function(className){
7751             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7752         },
7753
7754         /**
7755          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7756          * @param {String} oldClassName The CSS class to replace
7757          * @param {String} newClassName The replacement CSS class
7758          * @return {Roo.Element} this
7759          */
7760         replaceClass : function(oldClassName, newClassName){
7761             this.removeClass(oldClassName);
7762             this.addClass(newClassName);
7763             return this;
7764         },
7765
7766         /**
7767          * Returns an object with properties matching the styles requested.
7768          * For example, el.getStyles('color', 'font-size', 'width') might return
7769          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7770          * @param {String} style1 A style name
7771          * @param {String} style2 A style name
7772          * @param {String} etc.
7773          * @return {Object} The style object
7774          */
7775         getStyles : function(){
7776             var a = arguments, len = a.length, r = {};
7777             for(var i = 0; i < len; i++){
7778                 r[a[i]] = this.getStyle(a[i]);
7779             }
7780             return r;
7781         },
7782
7783         /**
7784          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7785          * @param {String} property The style property whose value is returned.
7786          * @return {String} The current value of the style property for this element.
7787          */
7788         getStyle : function(){
7789             return view && view.getComputedStyle ?
7790                 function(prop){
7791                     var el = this.dom, v, cs, camel;
7792                     if(prop == 'float'){
7793                         prop = "cssFloat";
7794                     }
7795                     if(el.style && (v = el.style[prop])){
7796                         return v;
7797                     }
7798                     if(cs = view.getComputedStyle(el, "")){
7799                         if(!(camel = propCache[prop])){
7800                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7801                         }
7802                         return cs[camel];
7803                     }
7804                     return null;
7805                 } :
7806                 function(prop){
7807                     var el = this.dom, v, cs, camel;
7808                     if(prop == 'opacity'){
7809                         if(typeof el.style.filter == 'string'){
7810                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7811                             if(m){
7812                                 var fv = parseFloat(m[1]);
7813                                 if(!isNaN(fv)){
7814                                     return fv ? fv / 100 : 0;
7815                                 }
7816                             }
7817                         }
7818                         return 1;
7819                     }else if(prop == 'float'){
7820                         prop = "styleFloat";
7821                     }
7822                     if(!(camel = propCache[prop])){
7823                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7824                     }
7825                     if(v = el.style[camel]){
7826                         return v;
7827                     }
7828                     if(cs = el.currentStyle){
7829                         return cs[camel];
7830                     }
7831                     return null;
7832                 };
7833         }(),
7834
7835         /**
7836          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7837          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7838          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7839          * @return {Roo.Element} this
7840          */
7841         setStyle : function(prop, value){
7842             if(typeof prop == "string"){
7843                 
7844                 if (prop == 'float') {
7845                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7846                     return this;
7847                 }
7848                 
7849                 var camel;
7850                 if(!(camel = propCache[prop])){
7851                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7852                 }
7853                 
7854                 if(camel == 'opacity') {
7855                     this.setOpacity(value);
7856                 }else{
7857                     this.dom.style[camel] = value;
7858                 }
7859             }else{
7860                 for(var style in prop){
7861                     if(typeof prop[style] != "function"){
7862                        this.setStyle(style, prop[style]);
7863                     }
7864                 }
7865             }
7866             return this;
7867         },
7868
7869         /**
7870          * More flexible version of {@link #setStyle} for setting style properties.
7871          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7872          * a function which returns such a specification.
7873          * @return {Roo.Element} this
7874          */
7875         applyStyles : function(style){
7876             Roo.DomHelper.applyStyles(this.dom, style);
7877             return this;
7878         },
7879
7880         /**
7881           * 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).
7882           * @return {Number} The X position of the element
7883           */
7884         getX : function(){
7885             return D.getX(this.dom);
7886         },
7887
7888         /**
7889           * 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).
7890           * @return {Number} The Y position of the element
7891           */
7892         getY : function(){
7893             return D.getY(this.dom);
7894         },
7895
7896         /**
7897           * 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).
7898           * @return {Array} The XY position of the element
7899           */
7900         getXY : function(){
7901             return D.getXY(this.dom);
7902         },
7903
7904         /**
7905          * 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).
7906          * @param {Number} The X position of the element
7907          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7908          * @return {Roo.Element} this
7909          */
7910         setX : function(x, animate){
7911             if(!animate || !A){
7912                 D.setX(this.dom, x);
7913             }else{
7914                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7915             }
7916             return this;
7917         },
7918
7919         /**
7920          * 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).
7921          * @param {Number} The Y position of the element
7922          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7923          * @return {Roo.Element} this
7924          */
7925         setY : function(y, animate){
7926             if(!animate || !A){
7927                 D.setY(this.dom, y);
7928             }else{
7929                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7930             }
7931             return this;
7932         },
7933
7934         /**
7935          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7936          * @param {String} left The left CSS property value
7937          * @return {Roo.Element} this
7938          */
7939         setLeft : function(left){
7940             this.setStyle("left", this.addUnits(left));
7941             return this;
7942         },
7943
7944         /**
7945          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7946          * @param {String} top The top CSS property value
7947          * @return {Roo.Element} this
7948          */
7949         setTop : function(top){
7950             this.setStyle("top", this.addUnits(top));
7951             return this;
7952         },
7953
7954         /**
7955          * Sets the element's CSS right style.
7956          * @param {String} right The right CSS property value
7957          * @return {Roo.Element} this
7958          */
7959         setRight : function(right){
7960             this.setStyle("right", this.addUnits(right));
7961             return this;
7962         },
7963
7964         /**
7965          * Sets the element's CSS bottom style.
7966          * @param {String} bottom The bottom CSS property value
7967          * @return {Roo.Element} this
7968          */
7969         setBottom : function(bottom){
7970             this.setStyle("bottom", this.addUnits(bottom));
7971             return this;
7972         },
7973
7974         /**
7975          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7976          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7977          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7978          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7979          * @return {Roo.Element} this
7980          */
7981         setXY : function(pos, animate){
7982             if(!animate || !A){
7983                 D.setXY(this.dom, pos);
7984             }else{
7985                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7986             }
7987             return this;
7988         },
7989
7990         /**
7991          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7992          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7993          * @param {Number} x X value for new position (coordinates are page-based)
7994          * @param {Number} y Y value for new position (coordinates are page-based)
7995          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7996          * @return {Roo.Element} this
7997          */
7998         setLocation : function(x, y, animate){
7999             this.setXY([x, y], this.preanim(arguments, 2));
8000             return this;
8001         },
8002
8003         /**
8004          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8005          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8006          * @param {Number} x X value for new position (coordinates are page-based)
8007          * @param {Number} y Y value for new position (coordinates are page-based)
8008          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8009          * @return {Roo.Element} this
8010          */
8011         moveTo : function(x, y, animate){
8012             this.setXY([x, y], this.preanim(arguments, 2));
8013             return this;
8014         },
8015
8016         /**
8017          * Returns the region of the given element.
8018          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8019          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8020          */
8021         getRegion : function(){
8022             return D.getRegion(this.dom);
8023         },
8024
8025         /**
8026          * Returns the offset height of the element
8027          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8028          * @return {Number} The element's height
8029          */
8030         getHeight : function(contentHeight){
8031             var h = this.dom.offsetHeight || 0;
8032             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8033         },
8034
8035         /**
8036          * Returns the offset width of the element
8037          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8038          * @return {Number} The element's width
8039          */
8040         getWidth : function(contentWidth){
8041             var w = this.dom.offsetWidth || 0;
8042             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8043         },
8044
8045         /**
8046          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8047          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8048          * if a height has not been set using CSS.
8049          * @return {Number}
8050          */
8051         getComputedHeight : function(){
8052             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8053             if(!h){
8054                 h = parseInt(this.getStyle('height'), 10) || 0;
8055                 if(!this.isBorderBox()){
8056                     h += this.getFrameWidth('tb');
8057                 }
8058             }
8059             return h;
8060         },
8061
8062         /**
8063          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8064          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8065          * if a width has not been set using CSS.
8066          * @return {Number}
8067          */
8068         getComputedWidth : function(){
8069             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8070             if(!w){
8071                 w = parseInt(this.getStyle('width'), 10) || 0;
8072                 if(!this.isBorderBox()){
8073                     w += this.getFrameWidth('lr');
8074                 }
8075             }
8076             return w;
8077         },
8078
8079         /**
8080          * Returns the size of the element.
8081          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8082          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8083          */
8084         getSize : function(contentSize){
8085             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8086         },
8087
8088         /**
8089          * Returns the width and height of the viewport.
8090          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8091          */
8092         getViewSize : function(){
8093             var d = this.dom, doc = document, aw = 0, ah = 0;
8094             if(d == doc || d == doc.body){
8095                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8096             }else{
8097                 return {
8098                     width : d.clientWidth,
8099                     height: d.clientHeight
8100                 };
8101             }
8102         },
8103
8104         /**
8105          * Returns the value of the "value" attribute
8106          * @param {Boolean} asNumber true to parse the value as a number
8107          * @return {String/Number}
8108          */
8109         getValue : function(asNumber){
8110             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8111         },
8112
8113         // private
8114         adjustWidth : function(width){
8115             if(typeof width == "number"){
8116                 if(this.autoBoxAdjust && !this.isBorderBox()){
8117                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8118                 }
8119                 if(width < 0){
8120                     width = 0;
8121                 }
8122             }
8123             return width;
8124         },
8125
8126         // private
8127         adjustHeight : function(height){
8128             if(typeof height == "number"){
8129                if(this.autoBoxAdjust && !this.isBorderBox()){
8130                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8131                }
8132                if(height < 0){
8133                    height = 0;
8134                }
8135             }
8136             return height;
8137         },
8138
8139         /**
8140          * Set the width of the element
8141          * @param {Number} width The new width
8142          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8143          * @return {Roo.Element} this
8144          */
8145         setWidth : function(width, animate){
8146             width = this.adjustWidth(width);
8147             if(!animate || !A){
8148                 this.dom.style.width = this.addUnits(width);
8149             }else{
8150                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8151             }
8152             return this;
8153         },
8154
8155         /**
8156          * Set the height of the element
8157          * @param {Number} height The new height
8158          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8159          * @return {Roo.Element} this
8160          */
8161          setHeight : function(height, animate){
8162             height = this.adjustHeight(height);
8163             if(!animate || !A){
8164                 this.dom.style.height = this.addUnits(height);
8165             }else{
8166                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8167             }
8168             return this;
8169         },
8170
8171         /**
8172          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8173          * @param {Number} width The new width
8174          * @param {Number} height The new height
8175          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8176          * @return {Roo.Element} this
8177          */
8178          setSize : function(width, height, animate){
8179             if(typeof width == "object"){ // in case of object from getSize()
8180                 height = width.height; width = width.width;
8181             }
8182             width = this.adjustWidth(width); height = this.adjustHeight(height);
8183             if(!animate || !A){
8184                 this.dom.style.width = this.addUnits(width);
8185                 this.dom.style.height = this.addUnits(height);
8186             }else{
8187                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8188             }
8189             return this;
8190         },
8191
8192         /**
8193          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8194          * @param {Number} x X value for new position (coordinates are page-based)
8195          * @param {Number} y Y value for new position (coordinates are page-based)
8196          * @param {Number} width The new width
8197          * @param {Number} height The new height
8198          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8199          * @return {Roo.Element} this
8200          */
8201         setBounds : function(x, y, width, height, animate){
8202             if(!animate || !A){
8203                 this.setSize(width, height);
8204                 this.setLocation(x, y);
8205             }else{
8206                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8207                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8208                               this.preanim(arguments, 4), 'motion');
8209             }
8210             return this;
8211         },
8212
8213         /**
8214          * 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.
8215          * @param {Roo.lib.Region} region The region to fill
8216          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8217          * @return {Roo.Element} this
8218          */
8219         setRegion : function(region, animate){
8220             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8221             return this;
8222         },
8223
8224         /**
8225          * Appends an event handler
8226          *
8227          * @param {String}   eventName     The type of event to append
8228          * @param {Function} fn        The method the event invokes
8229          * @param {Object} scope       (optional) The scope (this object) of the fn
8230          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8231          */
8232         addListener : function(eventName, fn, scope, options){
8233             if (this.dom) {
8234                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8235             }
8236             if (eventName == 'dblclick') {
8237                 this.addListener('touchstart', this.onTapHandler, this);
8238             }
8239         },
8240         tapedTwice : false,
8241         onTapHandler : function(event)
8242         {
8243             if(!this.tapedTwice) {
8244                 this.tapedTwice = true;
8245                 var s = this;
8246                 setTimeout( function() {
8247                     s.tapedTwice = false;
8248                 }, 300 );
8249                 return;
8250             }
8251             event.preventDefault();
8252             var revent = new MouseEvent('dblclick',  {
8253                 view: window,
8254                 bubbles: true,
8255                 cancelable: true
8256             });
8257              
8258             this.dom.dispatchEvent(revent);
8259             //action on double tap goes below
8260              
8261         }, 
8262
8263         /**
8264          * Removes an event handler from this element
8265          * @param {String} eventName the type of event to remove
8266          * @param {Function} fn the method the event invokes
8267          * @return {Roo.Element} this
8268          */
8269         removeListener : function(eventName, fn){
8270             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8271             return this;
8272         },
8273
8274         /**
8275          * Removes all previous added listeners from this element
8276          * @return {Roo.Element} this
8277          */
8278         removeAllListeners : function(){
8279             E.purgeElement(this.dom);
8280             return this;
8281         },
8282
8283         relayEvent : function(eventName, observable){
8284             this.on(eventName, function(e){
8285                 observable.fireEvent(eventName, e);
8286             });
8287         },
8288
8289         /**
8290          * Set the opacity of the element
8291          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8292          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8293          * @return {Roo.Element} this
8294          */
8295          setOpacity : function(opacity, animate){
8296             if(!animate || !A){
8297                 var s = this.dom.style;
8298                 if(Roo.isIE){
8299                     s.zoom = 1;
8300                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8301                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8302                 }else{
8303                     s.opacity = opacity;
8304                 }
8305             }else{
8306                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8307             }
8308             return this;
8309         },
8310
8311         /**
8312          * Gets the left X coordinate
8313          * @param {Boolean} local True to get the local css position instead of page coordinate
8314          * @return {Number}
8315          */
8316         getLeft : function(local){
8317             if(!local){
8318                 return this.getX();
8319             }else{
8320                 return parseInt(this.getStyle("left"), 10) || 0;
8321             }
8322         },
8323
8324         /**
8325          * Gets the right X coordinate of the element (element X position + element width)
8326          * @param {Boolean} local True to get the local css position instead of page coordinate
8327          * @return {Number}
8328          */
8329         getRight : function(local){
8330             if(!local){
8331                 return this.getX() + this.getWidth();
8332             }else{
8333                 return (this.getLeft(true) + this.getWidth()) || 0;
8334             }
8335         },
8336
8337         /**
8338          * Gets the top Y coordinate
8339          * @param {Boolean} local True to get the local css position instead of page coordinate
8340          * @return {Number}
8341          */
8342         getTop : function(local) {
8343             if(!local){
8344                 return this.getY();
8345             }else{
8346                 return parseInt(this.getStyle("top"), 10) || 0;
8347             }
8348         },
8349
8350         /**
8351          * Gets the bottom Y coordinate of the element (element Y position + element height)
8352          * @param {Boolean} local True to get the local css position instead of page coordinate
8353          * @return {Number}
8354          */
8355         getBottom : function(local){
8356             if(!local){
8357                 return this.getY() + this.getHeight();
8358             }else{
8359                 return (this.getTop(true) + this.getHeight()) || 0;
8360             }
8361         },
8362
8363         /**
8364         * Initializes positioning on this element. If a desired position is not passed, it will make the
8365         * the element positioned relative IF it is not already positioned.
8366         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8367         * @param {Number} zIndex (optional) The zIndex to apply
8368         * @param {Number} x (optional) Set the page X position
8369         * @param {Number} y (optional) Set the page Y position
8370         */
8371         position : function(pos, zIndex, x, y){
8372             if(!pos){
8373                if(this.getStyle('position') == 'static'){
8374                    this.setStyle('position', 'relative');
8375                }
8376             }else{
8377                 this.setStyle("position", pos);
8378             }
8379             if(zIndex){
8380                 this.setStyle("z-index", zIndex);
8381             }
8382             if(x !== undefined && y !== undefined){
8383                 this.setXY([x, y]);
8384             }else if(x !== undefined){
8385                 this.setX(x);
8386             }else if(y !== undefined){
8387                 this.setY(y);
8388             }
8389         },
8390
8391         /**
8392         * Clear positioning back to the default when the document was loaded
8393         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8394         * @return {Roo.Element} this
8395          */
8396         clearPositioning : function(value){
8397             value = value ||'';
8398             this.setStyle({
8399                 "left": value,
8400                 "right": value,
8401                 "top": value,
8402                 "bottom": value,
8403                 "z-index": "",
8404                 "position" : "static"
8405             });
8406             return this;
8407         },
8408
8409         /**
8410         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8411         * snapshot before performing an update and then restoring the element.
8412         * @return {Object}
8413         */
8414         getPositioning : function(){
8415             var l = this.getStyle("left");
8416             var t = this.getStyle("top");
8417             return {
8418                 "position" : this.getStyle("position"),
8419                 "left" : l,
8420                 "right" : l ? "" : this.getStyle("right"),
8421                 "top" : t,
8422                 "bottom" : t ? "" : this.getStyle("bottom"),
8423                 "z-index" : this.getStyle("z-index")
8424             };
8425         },
8426
8427         /**
8428          * Gets the width of the border(s) for the specified side(s)
8429          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8430          * passing lr would get the border (l)eft width + the border (r)ight width.
8431          * @return {Number} The width of the sides passed added together
8432          */
8433         getBorderWidth : function(side){
8434             return this.addStyles(side, El.borders);
8435         },
8436
8437         /**
8438          * Gets the width of the padding(s) for the specified side(s)
8439          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8440          * passing lr would get the padding (l)eft + the padding (r)ight.
8441          * @return {Number} The padding of the sides passed added together
8442          */
8443         getPadding : function(side){
8444             return this.addStyles(side, El.paddings);
8445         },
8446
8447         /**
8448         * Set positioning with an object returned by getPositioning().
8449         * @param {Object} posCfg
8450         * @return {Roo.Element} this
8451          */
8452         setPositioning : function(pc){
8453             this.applyStyles(pc);
8454             if(pc.right == "auto"){
8455                 this.dom.style.right = "";
8456             }
8457             if(pc.bottom == "auto"){
8458                 this.dom.style.bottom = "";
8459             }
8460             return this;
8461         },
8462
8463         // private
8464         fixDisplay : function(){
8465             if(this.getStyle("display") == "none"){
8466                 this.setStyle("visibility", "hidden");
8467                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8468                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8469                     this.setStyle("display", "block");
8470                 }
8471             }
8472         },
8473
8474         /**
8475          * Quick set left and top adding default units
8476          * @param {String} left The left CSS property value
8477          * @param {String} top The top CSS property value
8478          * @return {Roo.Element} this
8479          */
8480          setLeftTop : function(left, top){
8481             this.dom.style.left = this.addUnits(left);
8482             this.dom.style.top = this.addUnits(top);
8483             return this;
8484         },
8485
8486         /**
8487          * Move this element relative to its current position.
8488          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8489          * @param {Number} distance How far to move the element in pixels
8490          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8491          * @return {Roo.Element} this
8492          */
8493          move : function(direction, distance, animate){
8494             var xy = this.getXY();
8495             direction = direction.toLowerCase();
8496             switch(direction){
8497                 case "l":
8498                 case "left":
8499                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8500                     break;
8501                case "r":
8502                case "right":
8503                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8504                     break;
8505                case "t":
8506                case "top":
8507                case "up":
8508                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8509                     break;
8510                case "b":
8511                case "bottom":
8512                case "down":
8513                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8514                     break;
8515             }
8516             return this;
8517         },
8518
8519         /**
8520          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8521          * @return {Roo.Element} this
8522          */
8523         clip : function(){
8524             if(!this.isClipped){
8525                this.isClipped = true;
8526                this.originalClip = {
8527                    "o": this.getStyle("overflow"),
8528                    "x": this.getStyle("overflow-x"),
8529                    "y": this.getStyle("overflow-y")
8530                };
8531                this.setStyle("overflow", "hidden");
8532                this.setStyle("overflow-x", "hidden");
8533                this.setStyle("overflow-y", "hidden");
8534             }
8535             return this;
8536         },
8537
8538         /**
8539          *  Return clipping (overflow) to original clipping before clip() was called
8540          * @return {Roo.Element} this
8541          */
8542         unclip : function(){
8543             if(this.isClipped){
8544                 this.isClipped = false;
8545                 var o = this.originalClip;
8546                 if(o.o){this.setStyle("overflow", o.o);}
8547                 if(o.x){this.setStyle("overflow-x", o.x);}
8548                 if(o.y){this.setStyle("overflow-y", o.y);}
8549             }
8550             return this;
8551         },
8552
8553
8554         /**
8555          * Gets the x,y coordinates specified by the anchor position on the element.
8556          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8557          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8558          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8559          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8560          * @return {Array} [x, y] An array containing the element's x and y coordinates
8561          */
8562         getAnchorXY : function(anchor, local, s){
8563             //Passing a different size is useful for pre-calculating anchors,
8564             //especially for anchored animations that change the el size.
8565
8566             var w, h, vp = false;
8567             if(!s){
8568                 var d = this.dom;
8569                 if(d == document.body || d == document){
8570                     vp = true;
8571                     w = D.getViewWidth(); h = D.getViewHeight();
8572                 }else{
8573                     w = this.getWidth(); h = this.getHeight();
8574                 }
8575             }else{
8576                 w = s.width;  h = s.height;
8577             }
8578             var x = 0, y = 0, r = Math.round;
8579             switch((anchor || "tl").toLowerCase()){
8580                 case "c":
8581                     x = r(w*.5);
8582                     y = r(h*.5);
8583                 break;
8584                 case "t":
8585                     x = r(w*.5);
8586                     y = 0;
8587                 break;
8588                 case "l":
8589                     x = 0;
8590                     y = r(h*.5);
8591                 break;
8592                 case "r":
8593                     x = w;
8594                     y = r(h*.5);
8595                 break;
8596                 case "b":
8597                     x = r(w*.5);
8598                     y = h;
8599                 break;
8600                 case "tl":
8601                     x = 0;
8602                     y = 0;
8603                 break;
8604                 case "bl":
8605                     x = 0;
8606                     y = h;
8607                 break;
8608                 case "br":
8609                     x = w;
8610                     y = h;
8611                 break;
8612                 case "tr":
8613                     x = w;
8614                     y = 0;
8615                 break;
8616             }
8617             if(local === true){
8618                 return [x, y];
8619             }
8620             if(vp){
8621                 var sc = this.getScroll();
8622                 return [x + sc.left, y + sc.top];
8623             }
8624             //Add the element's offset xy
8625             var o = this.getXY();
8626             return [x+o[0], y+o[1]];
8627         },
8628
8629         /**
8630          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8631          * supported position values.
8632          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8633          * @param {String} position The position to align to.
8634          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8635          * @return {Array} [x, y]
8636          */
8637         getAlignToXY : function(el, p, o)
8638         {
8639             el = Roo.get(el);
8640             var d = this.dom;
8641             if(!el.dom){
8642                 throw "Element.alignTo with an element that doesn't exist";
8643             }
8644             var c = false; //constrain to viewport
8645             var p1 = "", p2 = "";
8646             o = o || [0,0];
8647
8648             if(!p){
8649                 p = "tl-bl";
8650             }else if(p == "?"){
8651                 p = "tl-bl?";
8652             }else if(p.indexOf("-") == -1){
8653                 p = "tl-" + p;
8654             }
8655             p = p.toLowerCase();
8656             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8657             if(!m){
8658                throw "Element.alignTo with an invalid alignment " + p;
8659             }
8660             p1 = m[1]; p2 = m[2]; c = !!m[3];
8661
8662             //Subtract the aligned el's internal xy from the target's offset xy
8663             //plus custom offset to get the aligned el's new offset xy
8664             var a1 = this.getAnchorXY(p1, true);
8665             var a2 = el.getAnchorXY(p2, false);
8666             var x = a2[0] - a1[0] + o[0];
8667             var y = a2[1] - a1[1] + o[1];
8668             if(c){
8669                 //constrain the aligned el to viewport if necessary
8670                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8671                 // 5px of margin for ie
8672                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8673
8674                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8675                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8676                 //otherwise swap the aligned el to the opposite border of the target.
8677                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8678                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8679                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
8680                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8681
8682                var doc = document;
8683                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8684                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8685
8686                if((x+w) > dw + scrollX){
8687                     x = swapX ? r.left-w : dw+scrollX-w;
8688                 }
8689                if(x < scrollX){
8690                    x = swapX ? r.right : scrollX;
8691                }
8692                if((y+h) > dh + scrollY){
8693                     y = swapY ? r.top-h : dh+scrollY-h;
8694                 }
8695                if (y < scrollY){
8696                    y = swapY ? r.bottom : scrollY;
8697                }
8698             }
8699             return [x,y];
8700         },
8701
8702         // private
8703         getConstrainToXY : function(){
8704             var os = {top:0, left:0, bottom:0, right: 0};
8705
8706             return function(el, local, offsets, proposedXY){
8707                 el = Roo.get(el);
8708                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8709
8710                 var vw, vh, vx = 0, vy = 0;
8711                 if(el.dom == document.body || el.dom == document){
8712                     vw = Roo.lib.Dom.getViewWidth();
8713                     vh = Roo.lib.Dom.getViewHeight();
8714                 }else{
8715                     vw = el.dom.clientWidth;
8716                     vh = el.dom.clientHeight;
8717                     if(!local){
8718                         var vxy = el.getXY();
8719                         vx = vxy[0];
8720                         vy = vxy[1];
8721                     }
8722                 }
8723
8724                 var s = el.getScroll();
8725
8726                 vx += offsets.left + s.left;
8727                 vy += offsets.top + s.top;
8728
8729                 vw -= offsets.right;
8730                 vh -= offsets.bottom;
8731
8732                 var vr = vx+vw;
8733                 var vb = vy+vh;
8734
8735                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8736                 var x = xy[0], y = xy[1];
8737                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8738
8739                 // only move it if it needs it
8740                 var moved = false;
8741
8742                 // first validate right/bottom
8743                 if((x + w) > vr){
8744                     x = vr - w;
8745                     moved = true;
8746                 }
8747                 if((y + h) > vb){
8748                     y = vb - h;
8749                     moved = true;
8750                 }
8751                 // then make sure top/left isn't negative
8752                 if(x < vx){
8753                     x = vx;
8754                     moved = true;
8755                 }
8756                 if(y < vy){
8757                     y = vy;
8758                     moved = true;
8759                 }
8760                 return moved ? [x, y] : false;
8761             };
8762         }(),
8763
8764         // private
8765         adjustForConstraints : function(xy, parent, offsets){
8766             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8767         },
8768
8769         /**
8770          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8771          * document it aligns it to the viewport.
8772          * The position parameter is optional, and can be specified in any one of the following formats:
8773          * <ul>
8774          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8775          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8776          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8777          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8778          *   <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
8779          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8780          * </ul>
8781          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8782          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8783          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8784          * that specified in order to enforce the viewport constraints.
8785          * Following are all of the supported anchor positions:
8786     <pre>
8787     Value  Description
8788     -----  -----------------------------
8789     tl     The top left corner (default)
8790     t      The center of the top edge
8791     tr     The top right corner
8792     l      The center of the left edge
8793     c      In the center of the element
8794     r      The center of the right edge
8795     bl     The bottom left corner
8796     b      The center of the bottom edge
8797     br     The bottom right corner
8798     </pre>
8799     Example Usage:
8800     <pre><code>
8801     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8802     el.alignTo("other-el");
8803
8804     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8805     el.alignTo("other-el", "tr?");
8806
8807     // align the bottom right corner of el with the center left edge of other-el
8808     el.alignTo("other-el", "br-l?");
8809
8810     // align the center of el with the bottom left corner of other-el and
8811     // adjust the x position by -6 pixels (and the y position by 0)
8812     el.alignTo("other-el", "c-bl", [-6, 0]);
8813     </code></pre>
8814          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8815          * @param {String} position The position to align to.
8816          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8817          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8818          * @return {Roo.Element} this
8819          */
8820         alignTo : function(element, position, offsets, animate){
8821             var xy = this.getAlignToXY(element, position, offsets);
8822             this.setXY(xy, this.preanim(arguments, 3));
8823             return this;
8824         },
8825
8826         /**
8827          * Anchors an element to another element and realigns it when the window is resized.
8828          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8829          * @param {String} position The position to align to.
8830          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8831          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8832          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8833          * is a number, it is used as the buffer delay (defaults to 50ms).
8834          * @param {Function} callback The function to call after the animation finishes
8835          * @return {Roo.Element} this
8836          */
8837         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8838             var action = function(){
8839                 this.alignTo(el, alignment, offsets, animate);
8840                 Roo.callback(callback, this);
8841             };
8842             Roo.EventManager.onWindowResize(action, this);
8843             var tm = typeof monitorScroll;
8844             if(tm != 'undefined'){
8845                 Roo.EventManager.on(window, 'scroll', action, this,
8846                     {buffer: tm == 'number' ? monitorScroll : 50});
8847             }
8848             action.call(this); // align immediately
8849             return this;
8850         },
8851         /**
8852          * Clears any opacity settings from this element. Required in some cases for IE.
8853          * @return {Roo.Element} this
8854          */
8855         clearOpacity : function(){
8856             if (window.ActiveXObject) {
8857                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8858                     this.dom.style.filter = "";
8859                 }
8860             } else {
8861                 this.dom.style.opacity = "";
8862                 this.dom.style["-moz-opacity"] = "";
8863                 this.dom.style["-khtml-opacity"] = "";
8864             }
8865             return this;
8866         },
8867
8868         /**
8869          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8870          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8871          * @return {Roo.Element} this
8872          */
8873         hide : function(animate){
8874             this.setVisible(false, this.preanim(arguments, 0));
8875             return this;
8876         },
8877
8878         /**
8879         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8880         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8881          * @return {Roo.Element} this
8882          */
8883         show : function(animate){
8884             this.setVisible(true, this.preanim(arguments, 0));
8885             return this;
8886         },
8887
8888         /**
8889          * @private Test if size has a unit, otherwise appends the default
8890          */
8891         addUnits : function(size){
8892             return Roo.Element.addUnits(size, this.defaultUnit);
8893         },
8894
8895         /**
8896          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8897          * @return {Roo.Element} this
8898          */
8899         beginMeasure : function(){
8900             var el = this.dom;
8901             if(el.offsetWidth || el.offsetHeight){
8902                 return this; // offsets work already
8903             }
8904             var changed = [];
8905             var p = this.dom, b = document.body; // start with this element
8906             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8907                 var pe = Roo.get(p);
8908                 if(pe.getStyle('display') == 'none'){
8909                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8910                     p.style.visibility = "hidden";
8911                     p.style.display = "block";
8912                 }
8913                 p = p.parentNode;
8914             }
8915             this._measureChanged = changed;
8916             return this;
8917
8918         },
8919
8920         /**
8921          * Restores displays to before beginMeasure was called
8922          * @return {Roo.Element} this
8923          */
8924         endMeasure : function(){
8925             var changed = this._measureChanged;
8926             if(changed){
8927                 for(var i = 0, len = changed.length; i < len; i++) {
8928                     var r = changed[i];
8929                     r.el.style.visibility = r.visibility;
8930                     r.el.style.display = "none";
8931                 }
8932                 this._measureChanged = null;
8933             }
8934             return this;
8935         },
8936
8937         /**
8938         * Update the innerHTML of this element, optionally searching for and processing scripts
8939         * @param {String} html The new HTML
8940         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8941         * @param {Function} callback For async script loading you can be noticed when the update completes
8942         * @return {Roo.Element} this
8943          */
8944         update : function(html, loadScripts, callback){
8945             if(typeof html == "undefined"){
8946                 html = "";
8947             }
8948             if(loadScripts !== true){
8949                 this.dom.innerHTML = html;
8950                 if(typeof callback == "function"){
8951                     callback();
8952                 }
8953                 return this;
8954             }
8955             var id = Roo.id();
8956             var dom = this.dom;
8957
8958             html += '<span id="' + id + '"></span>';
8959
8960             E.onAvailable(id, function(){
8961                 var hd = document.getElementsByTagName("head")[0];
8962                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8963                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8964                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8965
8966                 var match;
8967                 while(match = re.exec(html)){
8968                     var attrs = match[1];
8969                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8970                     if(srcMatch && srcMatch[2]){
8971                        var s = document.createElement("script");
8972                        s.src = srcMatch[2];
8973                        var typeMatch = attrs.match(typeRe);
8974                        if(typeMatch && typeMatch[2]){
8975                            s.type = typeMatch[2];
8976                        }
8977                        hd.appendChild(s);
8978                     }else if(match[2] && match[2].length > 0){
8979                         if(window.execScript) {
8980                            window.execScript(match[2]);
8981                         } else {
8982                             /**
8983                              * eval:var:id
8984                              * eval:var:dom
8985                              * eval:var:html
8986                              * 
8987                              */
8988                            window.eval(match[2]);
8989                         }
8990                     }
8991                 }
8992                 var el = document.getElementById(id);
8993                 if(el){el.parentNode.removeChild(el);}
8994                 if(typeof callback == "function"){
8995                     callback();
8996                 }
8997             });
8998             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8999             return this;
9000         },
9001
9002         /**
9003          * Direct access to the UpdateManager update() method (takes the same parameters).
9004          * @param {String/Function} url The url for this request or a function to call to get the url
9005          * @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}
9006          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9007          * @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.
9008          * @return {Roo.Element} this
9009          */
9010         load : function(){
9011             var um = this.getUpdateManager();
9012             um.update.apply(um, arguments);
9013             return this;
9014         },
9015
9016         /**
9017         * Gets this element's UpdateManager
9018         * @return {Roo.UpdateManager} The UpdateManager
9019         */
9020         getUpdateManager : function(){
9021             if(!this.updateManager){
9022                 this.updateManager = new Roo.UpdateManager(this);
9023             }
9024             return this.updateManager;
9025         },
9026
9027         /**
9028          * Disables text selection for this element (normalized across browsers)
9029          * @return {Roo.Element} this
9030          */
9031         unselectable : function(){
9032             this.dom.unselectable = "on";
9033             this.swallowEvent("selectstart", true);
9034             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9035             this.addClass("x-unselectable");
9036             return this;
9037         },
9038
9039         /**
9040         * Calculates the x, y to center this element on the screen
9041         * @return {Array} The x, y values [x, y]
9042         */
9043         getCenterXY : function(){
9044             return this.getAlignToXY(document, 'c-c');
9045         },
9046
9047         /**
9048         * Centers the Element in either the viewport, or another Element.
9049         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9050         */
9051         center : function(centerIn){
9052             this.alignTo(centerIn || document, 'c-c');
9053             return this;
9054         },
9055
9056         /**
9057          * Tests various css rules/browsers to determine if this element uses a border box
9058          * @return {Boolean}
9059          */
9060         isBorderBox : function(){
9061             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9062         },
9063
9064         /**
9065          * Return a box {x, y, width, height} that can be used to set another elements
9066          * size/location to match this element.
9067          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9068          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9069          * @return {Object} box An object in the format {x, y, width, height}
9070          */
9071         getBox : function(contentBox, local){
9072             var xy;
9073             if(!local){
9074                 xy = this.getXY();
9075             }else{
9076                 var left = parseInt(this.getStyle("left"), 10) || 0;
9077                 var top = parseInt(this.getStyle("top"), 10) || 0;
9078                 xy = [left, top];
9079             }
9080             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9081             if(!contentBox){
9082                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9083             }else{
9084                 var l = this.getBorderWidth("l")+this.getPadding("l");
9085                 var r = this.getBorderWidth("r")+this.getPadding("r");
9086                 var t = this.getBorderWidth("t")+this.getPadding("t");
9087                 var b = this.getBorderWidth("b")+this.getPadding("b");
9088                 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)};
9089             }
9090             bx.right = bx.x + bx.width;
9091             bx.bottom = bx.y + bx.height;
9092             return bx;
9093         },
9094
9095         /**
9096          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9097          for more information about the sides.
9098          * @param {String} sides
9099          * @return {Number}
9100          */
9101         getFrameWidth : function(sides, onlyContentBox){
9102             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9103         },
9104
9105         /**
9106          * 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.
9107          * @param {Object} box The box to fill {x, y, width, height}
9108          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9109          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9110          * @return {Roo.Element} this
9111          */
9112         setBox : function(box, adjust, animate){
9113             var w = box.width, h = box.height;
9114             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9115                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9116                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9117             }
9118             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9119             return this;
9120         },
9121
9122         /**
9123          * Forces the browser to repaint this element
9124          * @return {Roo.Element} this
9125          */
9126          repaint : function(){
9127             var dom = this.dom;
9128             this.addClass("x-repaint");
9129             setTimeout(function(){
9130                 Roo.get(dom).removeClass("x-repaint");
9131             }, 1);
9132             return this;
9133         },
9134
9135         /**
9136          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9137          * then it returns the calculated width of the sides (see getPadding)
9138          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9139          * @return {Object/Number}
9140          */
9141         getMargins : function(side){
9142             if(!side){
9143                 return {
9144                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9145                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9146                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9147                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9148                 };
9149             }else{
9150                 return this.addStyles(side, El.margins);
9151              }
9152         },
9153
9154         // private
9155         addStyles : function(sides, styles){
9156             var val = 0, v, w;
9157             for(var i = 0, len = sides.length; i < len; i++){
9158                 v = this.getStyle(styles[sides.charAt(i)]);
9159                 if(v){
9160                      w = parseInt(v, 10);
9161                      if(w){ val += w; }
9162                 }
9163             }
9164             return val;
9165         },
9166
9167         /**
9168          * Creates a proxy element of this element
9169          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9170          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9171          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9172          * @return {Roo.Element} The new proxy element
9173          */
9174         createProxy : function(config, renderTo, matchBox){
9175             if(renderTo){
9176                 renderTo = Roo.getDom(renderTo);
9177             }else{
9178                 renderTo = document.body;
9179             }
9180             config = typeof config == "object" ?
9181                 config : {tag : "div", cls: config};
9182             var proxy = Roo.DomHelper.append(renderTo, config, true);
9183             if(matchBox){
9184                proxy.setBox(this.getBox());
9185             }
9186             return proxy;
9187         },
9188
9189         /**
9190          * Puts a mask over this element to disable user interaction. Requires core.css.
9191          * This method can only be applied to elements which accept child nodes.
9192          * @param {String} msg (optional) A message to display in the mask
9193          * @param {String} msgCls (optional) A css class to apply to the msg element
9194          * @return {Element} The mask  element
9195          */
9196         mask : function(msg, msgCls)
9197         {
9198             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9199                 this.setStyle("position", "relative");
9200             }
9201             if(!this._mask){
9202                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9203             }
9204             
9205             this.addClass("x-masked");
9206             this._mask.setDisplayed(true);
9207             
9208             // we wander
9209             var z = 0;
9210             var dom = this.dom;
9211             while (dom && dom.style) {
9212                 if (!isNaN(parseInt(dom.style.zIndex))) {
9213                     z = Math.max(z, parseInt(dom.style.zIndex));
9214                 }
9215                 dom = dom.parentNode;
9216             }
9217             // if we are masking the body - then it hides everything..
9218             if (this.dom == document.body) {
9219                 z = 1000000;
9220                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9221                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9222             }
9223            
9224             if(typeof msg == 'string'){
9225                 if(!this._maskMsg){
9226                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9227                         cls: "roo-el-mask-msg", 
9228                         cn: [
9229                             {
9230                                 tag: 'i',
9231                                 cls: 'fa fa-spinner fa-spin'
9232                             },
9233                             {
9234                                 tag: 'div'
9235                             }   
9236                         ]
9237                     }, true);
9238                 }
9239                 var mm = this._maskMsg;
9240                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9241                 if (mm.dom.lastChild) { // weird IE issue?
9242                     mm.dom.lastChild.innerHTML = msg;
9243                 }
9244                 mm.setDisplayed(true);
9245                 mm.center(this);
9246                 mm.setStyle('z-index', z + 102);
9247             }
9248             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9249                 this._mask.setHeight(this.getHeight());
9250             }
9251             this._mask.setStyle('z-index', z + 100);
9252             
9253             return this._mask;
9254         },
9255
9256         /**
9257          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9258          * it is cached for reuse.
9259          */
9260         unmask : function(removeEl){
9261             if(this._mask){
9262                 if(removeEl === true){
9263                     this._mask.remove();
9264                     delete this._mask;
9265                     if(this._maskMsg){
9266                         this._maskMsg.remove();
9267                         delete this._maskMsg;
9268                     }
9269                 }else{
9270                     this._mask.setDisplayed(false);
9271                     if(this._maskMsg){
9272                         this._maskMsg.setDisplayed(false);
9273                     }
9274                 }
9275             }
9276             this.removeClass("x-masked");
9277         },
9278
9279         /**
9280          * Returns true if this element is masked
9281          * @return {Boolean}
9282          */
9283         isMasked : function(){
9284             return this._mask && this._mask.isVisible();
9285         },
9286
9287         /**
9288          * Creates an iframe shim for this element to keep selects and other windowed objects from
9289          * showing through.
9290          * @return {Roo.Element} The new shim element
9291          */
9292         createShim : function(){
9293             var el = document.createElement('iframe');
9294             el.frameBorder = 'no';
9295             el.className = 'roo-shim';
9296             if(Roo.isIE && Roo.isSecure){
9297                 el.src = Roo.SSL_SECURE_URL;
9298             }
9299             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9300             shim.autoBoxAdjust = false;
9301             return shim;
9302         },
9303
9304         /**
9305          * Removes this element from the DOM and deletes it from the cache
9306          */
9307         remove : function(){
9308             if(this.dom.parentNode){
9309                 this.dom.parentNode.removeChild(this.dom);
9310             }
9311             delete El.cache[this.dom.id];
9312         },
9313
9314         /**
9315          * Sets up event handlers to add and remove a css class when the mouse is over this element
9316          * @param {String} className
9317          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9318          * mouseout events for children elements
9319          * @return {Roo.Element} this
9320          */
9321         addClassOnOver : function(className, preventFlicker){
9322             this.on("mouseover", function(){
9323                 Roo.fly(this, '_internal').addClass(className);
9324             }, this.dom);
9325             var removeFn = function(e){
9326                 if(preventFlicker !== true || !e.within(this, true)){
9327                     Roo.fly(this, '_internal').removeClass(className);
9328                 }
9329             };
9330             this.on("mouseout", removeFn, this.dom);
9331             return this;
9332         },
9333
9334         /**
9335          * Sets up event handlers to add and remove a css class when this element has the focus
9336          * @param {String} className
9337          * @return {Roo.Element} this
9338          */
9339         addClassOnFocus : function(className){
9340             this.on("focus", function(){
9341                 Roo.fly(this, '_internal').addClass(className);
9342             }, this.dom);
9343             this.on("blur", function(){
9344                 Roo.fly(this, '_internal').removeClass(className);
9345             }, this.dom);
9346             return this;
9347         },
9348         /**
9349          * 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)
9350          * @param {String} className
9351          * @return {Roo.Element} this
9352          */
9353         addClassOnClick : function(className){
9354             var dom = this.dom;
9355             this.on("mousedown", function(){
9356                 Roo.fly(dom, '_internal').addClass(className);
9357                 var d = Roo.get(document);
9358                 var fn = function(){
9359                     Roo.fly(dom, '_internal').removeClass(className);
9360                     d.removeListener("mouseup", fn);
9361                 };
9362                 d.on("mouseup", fn);
9363             });
9364             return this;
9365         },
9366
9367         /**
9368          * Stops the specified event from bubbling and optionally prevents the default action
9369          * @param {String} eventName
9370          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9371          * @return {Roo.Element} this
9372          */
9373         swallowEvent : function(eventName, preventDefault){
9374             var fn = function(e){
9375                 e.stopPropagation();
9376                 if(preventDefault){
9377                     e.preventDefault();
9378                 }
9379             };
9380             if(eventName instanceof Array){
9381                 for(var i = 0, len = eventName.length; i < len; i++){
9382                      this.on(eventName[i], fn);
9383                 }
9384                 return this;
9385             }
9386             this.on(eventName, fn);
9387             return this;
9388         },
9389
9390         /**
9391          * @private
9392          */
9393         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9394
9395         /**
9396          * Sizes this element to its parent element's dimensions performing
9397          * neccessary box adjustments.
9398          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9399          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9400          * @return {Roo.Element} this
9401          */
9402         fitToParent : function(monitorResize, targetParent) {
9403           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9404           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9405           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9406             return;
9407           }
9408           var p = Roo.get(targetParent || this.dom.parentNode);
9409           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9410           if (monitorResize === true) {
9411             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9412             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9413           }
9414           return this;
9415         },
9416
9417         /**
9418          * Gets the next sibling, skipping text nodes
9419          * @return {HTMLElement} The next sibling or null
9420          */
9421         getNextSibling : function(){
9422             var n = this.dom.nextSibling;
9423             while(n && n.nodeType != 1){
9424                 n = n.nextSibling;
9425             }
9426             return n;
9427         },
9428
9429         /**
9430          * Gets the previous sibling, skipping text nodes
9431          * @return {HTMLElement} The previous sibling or null
9432          */
9433         getPrevSibling : function(){
9434             var n = this.dom.previousSibling;
9435             while(n && n.nodeType != 1){
9436                 n = n.previousSibling;
9437             }
9438             return n;
9439         },
9440
9441
9442         /**
9443          * Appends the passed element(s) to this element
9444          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9445          * @return {Roo.Element} this
9446          */
9447         appendChild: function(el){
9448             el = Roo.get(el);
9449             el.appendTo(this);
9450             return this;
9451         },
9452
9453         /**
9454          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9455          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9456          * automatically generated with the specified attributes.
9457          * @param {HTMLElement} insertBefore (optional) a child element of this element
9458          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9459          * @return {Roo.Element} The new child element
9460          */
9461         createChild: function(config, insertBefore, returnDom){
9462             config = config || {tag:'div'};
9463             if(insertBefore){
9464                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9465             }
9466             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9467         },
9468
9469         /**
9470          * Appends this element to the passed element
9471          * @param {String/HTMLElement/Element} el The new parent element
9472          * @return {Roo.Element} this
9473          */
9474         appendTo: function(el){
9475             el = Roo.getDom(el);
9476             el.appendChild(this.dom);
9477             return this;
9478         },
9479
9480         /**
9481          * Inserts this element before the passed element in the DOM
9482          * @param {String/HTMLElement/Element} el The element to insert before
9483          * @return {Roo.Element} this
9484          */
9485         insertBefore: function(el){
9486             el = Roo.getDom(el);
9487             el.parentNode.insertBefore(this.dom, el);
9488             return this;
9489         },
9490
9491         /**
9492          * Inserts this element after the passed element in the DOM
9493          * @param {String/HTMLElement/Element} el The element to insert after
9494          * @return {Roo.Element} this
9495          */
9496         insertAfter: function(el){
9497             el = Roo.getDom(el);
9498             el.parentNode.insertBefore(this.dom, el.nextSibling);
9499             return this;
9500         },
9501
9502         /**
9503          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9504          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9505          * @return {Roo.Element} The new child
9506          */
9507         insertFirst: function(el, returnDom){
9508             el = el || {};
9509             if(typeof el == 'object' && !el.nodeType){ // dh config
9510                 return this.createChild(el, this.dom.firstChild, returnDom);
9511             }else{
9512                 el = Roo.getDom(el);
9513                 this.dom.insertBefore(el, this.dom.firstChild);
9514                 return !returnDom ? Roo.get(el) : el;
9515             }
9516         },
9517
9518         /**
9519          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9520          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9521          * @param {String} where (optional) 'before' or 'after' defaults to before
9522          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9523          * @return {Roo.Element} the inserted Element
9524          */
9525         insertSibling: function(el, where, returnDom){
9526             where = where ? where.toLowerCase() : 'before';
9527             el = el || {};
9528             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9529
9530             if(typeof el == 'object' && !el.nodeType){ // dh config
9531                 if(where == 'after' && !this.dom.nextSibling){
9532                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9533                 }else{
9534                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9535                 }
9536
9537             }else{
9538                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9539                             where == 'before' ? this.dom : this.dom.nextSibling);
9540                 if(!returnDom){
9541                     rt = Roo.get(rt);
9542                 }
9543             }
9544             return rt;
9545         },
9546
9547         /**
9548          * Creates and wraps this element with another element
9549          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9550          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9551          * @return {HTMLElement/Element} The newly created wrapper element
9552          */
9553         wrap: function(config, returnDom){
9554             if(!config){
9555                 config = {tag: "div"};
9556             }
9557             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9558             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9559             return newEl;
9560         },
9561
9562         /**
9563          * Replaces the passed element with this element
9564          * @param {String/HTMLElement/Element} el The element to replace
9565          * @return {Roo.Element} this
9566          */
9567         replace: function(el){
9568             el = Roo.get(el);
9569             this.insertBefore(el);
9570             el.remove();
9571             return this;
9572         },
9573
9574         /**
9575          * Inserts an html fragment into this element
9576          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9577          * @param {String} html The HTML fragment
9578          * @param {Boolean} returnEl True to return an Roo.Element
9579          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9580          */
9581         insertHtml : function(where, html, returnEl){
9582             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9583             return returnEl ? Roo.get(el) : el;
9584         },
9585
9586         /**
9587          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9588          * @param {Object} o The object with the attributes
9589          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9590          * @return {Roo.Element} this
9591          */
9592         set : function(o, useSet){
9593             var el = this.dom;
9594             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9595             for(var attr in o){
9596                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9597                 if(attr=="cls"){
9598                     el.className = o["cls"];
9599                 }else{
9600                     if(useSet) {
9601                         el.setAttribute(attr, o[attr]);
9602                     } else {
9603                         el[attr] = o[attr];
9604                     }
9605                 }
9606             }
9607             if(o.style){
9608                 Roo.DomHelper.applyStyles(el, o.style);
9609             }
9610             return this;
9611         },
9612
9613         /**
9614          * Convenience method for constructing a KeyMap
9615          * @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:
9616          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9617          * @param {Function} fn The function to call
9618          * @param {Object} scope (optional) The scope of the function
9619          * @return {Roo.KeyMap} The KeyMap created
9620          */
9621         addKeyListener : function(key, fn, scope){
9622             var config;
9623             if(typeof key != "object" || key instanceof Array){
9624                 config = {
9625                     key: key,
9626                     fn: fn,
9627                     scope: scope
9628                 };
9629             }else{
9630                 config = {
9631                     key : key.key,
9632                     shift : key.shift,
9633                     ctrl : key.ctrl,
9634                     alt : key.alt,
9635                     fn: fn,
9636                     scope: scope
9637                 };
9638             }
9639             return new Roo.KeyMap(this, config);
9640         },
9641
9642         /**
9643          * Creates a KeyMap for this element
9644          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9645          * @return {Roo.KeyMap} The KeyMap created
9646          */
9647         addKeyMap : function(config){
9648             return new Roo.KeyMap(this, config);
9649         },
9650
9651         /**
9652          * Returns true if this element is scrollable.
9653          * @return {Boolean}
9654          */
9655          isScrollable : function(){
9656             var dom = this.dom;
9657             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9658         },
9659
9660         /**
9661          * 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().
9662          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9663          * @param {Number} value The new scroll value
9664          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9665          * @return {Element} this
9666          */
9667
9668         scrollTo : function(side, value, animate){
9669             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9670             if(!animate || !A){
9671                 this.dom[prop] = value;
9672             }else{
9673                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9674                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9675             }
9676             return this;
9677         },
9678
9679         /**
9680          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9681          * within this element's scrollable range.
9682          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9683          * @param {Number} distance How far to scroll the element in pixels
9684          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9685          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9686          * was scrolled as far as it could go.
9687          */
9688          scroll : function(direction, distance, animate){
9689              if(!this.isScrollable()){
9690                  return;
9691              }
9692              var el = this.dom;
9693              var l = el.scrollLeft, t = el.scrollTop;
9694              var w = el.scrollWidth, h = el.scrollHeight;
9695              var cw = el.clientWidth, ch = el.clientHeight;
9696              direction = direction.toLowerCase();
9697              var scrolled = false;
9698              var a = this.preanim(arguments, 2);
9699              switch(direction){
9700                  case "l":
9701                  case "left":
9702                      if(w - l > cw){
9703                          var v = Math.min(l + distance, w-cw);
9704                          this.scrollTo("left", v, a);
9705                          scrolled = true;
9706                      }
9707                      break;
9708                 case "r":
9709                 case "right":
9710                      if(l > 0){
9711                          var v = Math.max(l - distance, 0);
9712                          this.scrollTo("left", v, a);
9713                          scrolled = true;
9714                      }
9715                      break;
9716                 case "t":
9717                 case "top":
9718                 case "up":
9719                      if(t > 0){
9720                          var v = Math.max(t - distance, 0);
9721                          this.scrollTo("top", v, a);
9722                          scrolled = true;
9723                      }
9724                      break;
9725                 case "b":
9726                 case "bottom":
9727                 case "down":
9728                      if(h - t > ch){
9729                          var v = Math.min(t + distance, h-ch);
9730                          this.scrollTo("top", v, a);
9731                          scrolled = true;
9732                      }
9733                      break;
9734              }
9735              return scrolled;
9736         },
9737
9738         /**
9739          * Translates the passed page coordinates into left/top css values for this element
9740          * @param {Number/Array} x The page x or an array containing [x, y]
9741          * @param {Number} y The page y
9742          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9743          */
9744         translatePoints : function(x, y){
9745             if(typeof x == 'object' || x instanceof Array){
9746                 y = x[1]; x = x[0];
9747             }
9748             var p = this.getStyle('position');
9749             var o = this.getXY();
9750
9751             var l = parseInt(this.getStyle('left'), 10);
9752             var t = parseInt(this.getStyle('top'), 10);
9753
9754             if(isNaN(l)){
9755                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9756             }
9757             if(isNaN(t)){
9758                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9759             }
9760
9761             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9762         },
9763
9764         /**
9765          * Returns the current scroll position of the element.
9766          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9767          */
9768         getScroll : function(){
9769             var d = this.dom, doc = document;
9770             if(d == doc || d == doc.body){
9771                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9772                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9773                 return {left: l, top: t};
9774             }else{
9775                 return {left: d.scrollLeft, top: d.scrollTop};
9776             }
9777         },
9778
9779         /**
9780          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9781          * are convert to standard 6 digit hex color.
9782          * @param {String} attr The css attribute
9783          * @param {String} defaultValue The default value to use when a valid color isn't found
9784          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9785          * YUI color anims.
9786          */
9787         getColor : function(attr, defaultValue, prefix){
9788             var v = this.getStyle(attr);
9789             if(!v || v == "transparent" || v == "inherit") {
9790                 return defaultValue;
9791             }
9792             var color = typeof prefix == "undefined" ? "#" : prefix;
9793             if(v.substr(0, 4) == "rgb("){
9794                 var rvs = v.slice(4, v.length -1).split(",");
9795                 for(var i = 0; i < 3; i++){
9796                     var h = parseInt(rvs[i]).toString(16);
9797                     if(h < 16){
9798                         h = "0" + h;
9799                     }
9800                     color += h;
9801                 }
9802             } else {
9803                 if(v.substr(0, 1) == "#"){
9804                     if(v.length == 4) {
9805                         for(var i = 1; i < 4; i++){
9806                             var c = v.charAt(i);
9807                             color +=  c + c;
9808                         }
9809                     }else if(v.length == 7){
9810                         color += v.substr(1);
9811                     }
9812                 }
9813             }
9814             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9815         },
9816
9817         /**
9818          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9819          * gradient background, rounded corners and a 4-way shadow.
9820          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9821          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9822          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9823          * @return {Roo.Element} this
9824          */
9825         boxWrap : function(cls){
9826             cls = cls || 'x-box';
9827             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9828             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9829             return el;
9830         },
9831
9832         /**
9833          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9834          * @param {String} namespace The namespace in which to look for the attribute
9835          * @param {String} name The attribute name
9836          * @return {String} The attribute value
9837          */
9838         getAttributeNS : Roo.isIE ? function(ns, name){
9839             var d = this.dom;
9840             var type = typeof d[ns+":"+name];
9841             if(type != 'undefined' && type != 'unknown'){
9842                 return d[ns+":"+name];
9843             }
9844             return d[name];
9845         } : function(ns, name){
9846             var d = this.dom;
9847             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9848         },
9849         
9850         
9851         /**
9852          * Sets or Returns the value the dom attribute value
9853          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9854          * @param {String} value (optional) The value to set the attribute to
9855          * @return {String} The attribute value
9856          */
9857         attr : function(name){
9858             if (arguments.length > 1) {
9859                 this.dom.setAttribute(name, arguments[1]);
9860                 return arguments[1];
9861             }
9862             if (typeof(name) == 'object') {
9863                 for(var i in name) {
9864                     this.attr(i, name[i]);
9865                 }
9866                 return name;
9867             }
9868             
9869             
9870             if (!this.dom.hasAttribute(name)) {
9871                 return undefined;
9872             }
9873             return this.dom.getAttribute(name);
9874         }
9875         
9876         
9877         
9878     };
9879
9880     var ep = El.prototype;
9881
9882     /**
9883      * Appends an event handler (Shorthand for addListener)
9884      * @param {String}   eventName     The type of event to append
9885      * @param {Function} fn        The method the event invokes
9886      * @param {Object} scope       (optional) The scope (this object) of the fn
9887      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9888      * @method
9889      */
9890     ep.on = ep.addListener;
9891         // backwards compat
9892     ep.mon = ep.addListener;
9893
9894     /**
9895      * Removes an event handler from this element (shorthand for removeListener)
9896      * @param {String} eventName the type of event to remove
9897      * @param {Function} fn the method the event invokes
9898      * @return {Roo.Element} this
9899      * @method
9900      */
9901     ep.un = ep.removeListener;
9902
9903     /**
9904      * true to automatically adjust width and height settings for box-model issues (default to true)
9905      */
9906     ep.autoBoxAdjust = true;
9907
9908     // private
9909     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9910
9911     // private
9912     El.addUnits = function(v, defaultUnit){
9913         if(v === "" || v == "auto"){
9914             return v;
9915         }
9916         if(v === undefined){
9917             return '';
9918         }
9919         if(typeof v == "number" || !El.unitPattern.test(v)){
9920             return v + (defaultUnit || 'px');
9921         }
9922         return v;
9923     };
9924
9925     // special markup used throughout Roo when box wrapping elements
9926     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>';
9927     /**
9928      * Visibility mode constant - Use visibility to hide element
9929      * @static
9930      * @type Number
9931      */
9932     El.VISIBILITY = 1;
9933     /**
9934      * Visibility mode constant - Use display to hide element
9935      * @static
9936      * @type Number
9937      */
9938     El.DISPLAY = 2;
9939
9940     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9941     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9942     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9943
9944
9945
9946     /**
9947      * @private
9948      */
9949     El.cache = {};
9950
9951     var docEl;
9952
9953     /**
9954      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9955      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9956      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9957      * @return {Element} The Element object
9958      * @static
9959      */
9960     El.get = function(el){
9961         var ex, elm, id;
9962         if(!el){ return null; }
9963         if(typeof el == "string"){ // element id
9964             if(!(elm = document.getElementById(el))){
9965                 return null;
9966             }
9967             if(ex = El.cache[el]){
9968                 ex.dom = elm;
9969             }else{
9970                 ex = El.cache[el] = new El(elm);
9971             }
9972             return ex;
9973         }else if(el.tagName){ // dom element
9974             if(!(id = el.id)){
9975                 id = Roo.id(el);
9976             }
9977             if(ex = El.cache[id]){
9978                 ex.dom = el;
9979             }else{
9980                 ex = El.cache[id] = new El(el);
9981             }
9982             return ex;
9983         }else if(el instanceof El){
9984             if(el != docEl){
9985                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9986                                                               // catch case where it hasn't been appended
9987                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9988             }
9989             return el;
9990         }else if(el.isComposite){
9991             return el;
9992         }else if(el instanceof Array){
9993             return El.select(el);
9994         }else if(el == document){
9995             // create a bogus element object representing the document object
9996             if(!docEl){
9997                 var f = function(){};
9998                 f.prototype = El.prototype;
9999                 docEl = new f();
10000                 docEl.dom = document;
10001             }
10002             return docEl;
10003         }
10004         return null;
10005     };
10006
10007     // private
10008     El.uncache = function(el){
10009         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10010             if(a[i]){
10011                 delete El.cache[a[i].id || a[i]];
10012             }
10013         }
10014     };
10015
10016     // private
10017     // Garbage collection - uncache elements/purge listeners on orphaned elements
10018     // so we don't hold a reference and cause the browser to retain them
10019     El.garbageCollect = function(){
10020         if(!Roo.enableGarbageCollector){
10021             clearInterval(El.collectorThread);
10022             return;
10023         }
10024         for(var eid in El.cache){
10025             var el = El.cache[eid], d = el.dom;
10026             // -------------------------------------------------------
10027             // Determining what is garbage:
10028             // -------------------------------------------------------
10029             // !d
10030             // dom node is null, definitely garbage
10031             // -------------------------------------------------------
10032             // !d.parentNode
10033             // no parentNode == direct orphan, definitely garbage
10034             // -------------------------------------------------------
10035             // !d.offsetParent && !document.getElementById(eid)
10036             // display none elements have no offsetParent so we will
10037             // also try to look it up by it's id. However, check
10038             // offsetParent first so we don't do unneeded lookups.
10039             // This enables collection of elements that are not orphans
10040             // directly, but somewhere up the line they have an orphan
10041             // parent.
10042             // -------------------------------------------------------
10043             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10044                 delete El.cache[eid];
10045                 if(d && Roo.enableListenerCollection){
10046                     E.purgeElement(d);
10047                 }
10048             }
10049         }
10050     }
10051     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10052
10053
10054     // dom is optional
10055     El.Flyweight = function(dom){
10056         this.dom = dom;
10057     };
10058     El.Flyweight.prototype = El.prototype;
10059
10060     El._flyweights = {};
10061     /**
10062      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10063      * the dom node can be overwritten by other code.
10064      * @param {String/HTMLElement} el The dom node or id
10065      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10066      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10067      * @static
10068      * @return {Element} The shared Element object
10069      */
10070     El.fly = function(el, named){
10071         named = named || '_global';
10072         el = Roo.getDom(el);
10073         if(!el){
10074             return null;
10075         }
10076         if(!El._flyweights[named]){
10077             El._flyweights[named] = new El.Flyweight();
10078         }
10079         El._flyweights[named].dom = el;
10080         return El._flyweights[named];
10081     };
10082
10083     /**
10084      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10085      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10086      * Shorthand of {@link Roo.Element#get}
10087      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10088      * @return {Element} The Element object
10089      * @member Roo
10090      * @method get
10091      */
10092     Roo.get = El.get;
10093     /**
10094      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10095      * the dom node can be overwritten by other code.
10096      * Shorthand of {@link Roo.Element#fly}
10097      * @param {String/HTMLElement} el The dom node or id
10098      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10099      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10100      * @static
10101      * @return {Element} The shared Element object
10102      * @member Roo
10103      * @method fly
10104      */
10105     Roo.fly = El.fly;
10106
10107     // speedy lookup for elements never to box adjust
10108     var noBoxAdjust = Roo.isStrict ? {
10109         select:1
10110     } : {
10111         input:1, select:1, textarea:1
10112     };
10113     if(Roo.isIE || Roo.isGecko){
10114         noBoxAdjust['button'] = 1;
10115     }
10116
10117
10118     Roo.EventManager.on(window, 'unload', function(){
10119         delete El.cache;
10120         delete El._flyweights;
10121     });
10122 })();
10123
10124
10125
10126
10127 if(Roo.DomQuery){
10128     Roo.Element.selectorFunction = Roo.DomQuery.select;
10129 }
10130
10131 Roo.Element.select = function(selector, unique, root){
10132     var els;
10133     if(typeof selector == "string"){
10134         els = Roo.Element.selectorFunction(selector, root);
10135     }else if(selector.length !== undefined){
10136         els = selector;
10137     }else{
10138         throw "Invalid selector";
10139     }
10140     if(unique === true){
10141         return new Roo.CompositeElement(els);
10142     }else{
10143         return new Roo.CompositeElementLite(els);
10144     }
10145 };
10146 /**
10147  * Selects elements based on the passed CSS selector to enable working on them as 1.
10148  * @param {String/Array} selector The CSS selector or an array of elements
10149  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10150  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10151  * @return {CompositeElementLite/CompositeElement}
10152  * @member Roo
10153  * @method select
10154  */
10155 Roo.select = Roo.Element.select;
10156
10157
10158
10159
10160
10161
10162
10163
10164
10165
10166
10167
10168
10169
10170 /*
10171  * Based on:
10172  * Ext JS Library 1.1.1
10173  * Copyright(c) 2006-2007, Ext JS, LLC.
10174  *
10175  * Originally Released Under LGPL - original licence link has changed is not relivant.
10176  *
10177  * Fork - LGPL
10178  * <script type="text/javascript">
10179  */
10180
10181
10182
10183 //Notifies Element that fx methods are available
10184 Roo.enableFx = true;
10185
10186 /**
10187  * @class Roo.Fx
10188  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10189  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10190  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10191  * Element effects to work.</p><br/>
10192  *
10193  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10194  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10195  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10196  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10197  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10198  * expected results and should be done with care.</p><br/>
10199  *
10200  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10201  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10202 <pre>
10203 Value  Description
10204 -----  -----------------------------
10205 tl     The top left corner
10206 t      The center of the top edge
10207 tr     The top right corner
10208 l      The center of the left edge
10209 r      The center of the right edge
10210 bl     The bottom left corner
10211 b      The center of the bottom edge
10212 br     The bottom right corner
10213 </pre>
10214  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10215  * below are common options that can be passed to any Fx method.</b>
10216  * @cfg {Function} callback A function called when the effect is finished
10217  * @cfg {Object} scope The scope of the effect function
10218  * @cfg {String} easing A valid Easing value for the effect
10219  * @cfg {String} afterCls A css class to apply after the effect
10220  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10221  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10222  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10223  * effects that end with the element being visually hidden, ignored otherwise)
10224  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10225  * a function which returns such a specification that will be applied to the Element after the effect finishes
10226  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10227  * @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
10228  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10229  */
10230 Roo.Fx = {
10231         /**
10232          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10233          * origin for the slide effect.  This function automatically handles wrapping the element with
10234          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10235          * Usage:
10236          *<pre><code>
10237 // default: slide the element in from the top
10238 el.slideIn();
10239
10240 // custom: slide the element in from the right with a 2-second duration
10241 el.slideIn('r', { duration: 2 });
10242
10243 // common config options shown with default values
10244 el.slideIn('t', {
10245     easing: 'easeOut',
10246     duration: .5
10247 });
10248 </code></pre>
10249          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10250          * @param {Object} options (optional) Object literal with any of the Fx config options
10251          * @return {Roo.Element} The Element
10252          */
10253     slideIn : function(anchor, o){
10254         var el = this.getFxEl();
10255         o = o || {};
10256
10257         el.queueFx(o, function(){
10258
10259             anchor = anchor || "t";
10260
10261             // fix display to visibility
10262             this.fixDisplay();
10263
10264             // restore values after effect
10265             var r = this.getFxRestore();
10266             var b = this.getBox();
10267             // fixed size for slide
10268             this.setSize(b);
10269
10270             // wrap if needed
10271             var wrap = this.fxWrap(r.pos, o, "hidden");
10272
10273             var st = this.dom.style;
10274             st.visibility = "visible";
10275             st.position = "absolute";
10276
10277             // clear out temp styles after slide and unwrap
10278             var after = function(){
10279                 el.fxUnwrap(wrap, r.pos, o);
10280                 st.width = r.width;
10281                 st.height = r.height;
10282                 el.afterFx(o);
10283             };
10284             // time to calc the positions
10285             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10286
10287             switch(anchor.toLowerCase()){
10288                 case "t":
10289                     wrap.setSize(b.width, 0);
10290                     st.left = st.bottom = "0";
10291                     a = {height: bh};
10292                 break;
10293                 case "l":
10294                     wrap.setSize(0, b.height);
10295                     st.right = st.top = "0";
10296                     a = {width: bw};
10297                 break;
10298                 case "r":
10299                     wrap.setSize(0, b.height);
10300                     wrap.setX(b.right);
10301                     st.left = st.top = "0";
10302                     a = {width: bw, points: pt};
10303                 break;
10304                 case "b":
10305                     wrap.setSize(b.width, 0);
10306                     wrap.setY(b.bottom);
10307                     st.left = st.top = "0";
10308                     a = {height: bh, points: pt};
10309                 break;
10310                 case "tl":
10311                     wrap.setSize(0, 0);
10312                     st.right = st.bottom = "0";
10313                     a = {width: bw, height: bh};
10314                 break;
10315                 case "bl":
10316                     wrap.setSize(0, 0);
10317                     wrap.setY(b.y+b.height);
10318                     st.right = st.top = "0";
10319                     a = {width: bw, height: bh, points: pt};
10320                 break;
10321                 case "br":
10322                     wrap.setSize(0, 0);
10323                     wrap.setXY([b.right, b.bottom]);
10324                     st.left = st.top = "0";
10325                     a = {width: bw, height: bh, points: pt};
10326                 break;
10327                 case "tr":
10328                     wrap.setSize(0, 0);
10329                     wrap.setX(b.x+b.width);
10330                     st.left = st.bottom = "0";
10331                     a = {width: bw, height: bh, points: pt};
10332                 break;
10333             }
10334             this.dom.style.visibility = "visible";
10335             wrap.show();
10336
10337             arguments.callee.anim = wrap.fxanim(a,
10338                 o,
10339                 'motion',
10340                 .5,
10341                 'easeOut', after);
10342         });
10343         return this;
10344     },
10345     
10346         /**
10347          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10348          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10349          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10350          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10351          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10352          * Usage:
10353          *<pre><code>
10354 // default: slide the element out to the top
10355 el.slideOut();
10356
10357 // custom: slide the element out to the right with a 2-second duration
10358 el.slideOut('r', { duration: 2 });
10359
10360 // common config options shown with default values
10361 el.slideOut('t', {
10362     easing: 'easeOut',
10363     duration: .5,
10364     remove: false,
10365     useDisplay: false
10366 });
10367 </code></pre>
10368          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10369          * @param {Object} options (optional) Object literal with any of the Fx config options
10370          * @return {Roo.Element} The Element
10371          */
10372     slideOut : function(anchor, o){
10373         var el = this.getFxEl();
10374         o = o || {};
10375
10376         el.queueFx(o, function(){
10377
10378             anchor = anchor || "t";
10379
10380             // restore values after effect
10381             var r = this.getFxRestore();
10382             
10383             var b = this.getBox();
10384             // fixed size for slide
10385             this.setSize(b);
10386
10387             // wrap if needed
10388             var wrap = this.fxWrap(r.pos, o, "visible");
10389
10390             var st = this.dom.style;
10391             st.visibility = "visible";
10392             st.position = "absolute";
10393
10394             wrap.setSize(b);
10395
10396             var after = function(){
10397                 if(o.useDisplay){
10398                     el.setDisplayed(false);
10399                 }else{
10400                     el.hide();
10401                 }
10402
10403                 el.fxUnwrap(wrap, r.pos, o);
10404
10405                 st.width = r.width;
10406                 st.height = r.height;
10407
10408                 el.afterFx(o);
10409             };
10410
10411             var a, zero = {to: 0};
10412             switch(anchor.toLowerCase()){
10413                 case "t":
10414                     st.left = st.bottom = "0";
10415                     a = {height: zero};
10416                 break;
10417                 case "l":
10418                     st.right = st.top = "0";
10419                     a = {width: zero};
10420                 break;
10421                 case "r":
10422                     st.left = st.top = "0";
10423                     a = {width: zero, points: {to:[b.right, b.y]}};
10424                 break;
10425                 case "b":
10426                     st.left = st.top = "0";
10427                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10428                 break;
10429                 case "tl":
10430                     st.right = st.bottom = "0";
10431                     a = {width: zero, height: zero};
10432                 break;
10433                 case "bl":
10434                     st.right = st.top = "0";
10435                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10436                 break;
10437                 case "br":
10438                     st.left = st.top = "0";
10439                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10440                 break;
10441                 case "tr":
10442                     st.left = st.bottom = "0";
10443                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10444                 break;
10445             }
10446
10447             arguments.callee.anim = wrap.fxanim(a,
10448                 o,
10449                 'motion',
10450                 .5,
10451                 "easeOut", after);
10452         });
10453         return this;
10454     },
10455
10456         /**
10457          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10458          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10459          * The element must be removed from the DOM using the 'remove' config option if desired.
10460          * Usage:
10461          *<pre><code>
10462 // default
10463 el.puff();
10464
10465 // common config options shown with default values
10466 el.puff({
10467     easing: 'easeOut',
10468     duration: .5,
10469     remove: false,
10470     useDisplay: false
10471 });
10472 </code></pre>
10473          * @param {Object} options (optional) Object literal with any of the Fx config options
10474          * @return {Roo.Element} The Element
10475          */
10476     puff : function(o){
10477         var el = this.getFxEl();
10478         o = o || {};
10479
10480         el.queueFx(o, function(){
10481             this.clearOpacity();
10482             this.show();
10483
10484             // restore values after effect
10485             var r = this.getFxRestore();
10486             var st = this.dom.style;
10487
10488             var after = function(){
10489                 if(o.useDisplay){
10490                     el.setDisplayed(false);
10491                 }else{
10492                     el.hide();
10493                 }
10494
10495                 el.clearOpacity();
10496
10497                 el.setPositioning(r.pos);
10498                 st.width = r.width;
10499                 st.height = r.height;
10500                 st.fontSize = '';
10501                 el.afterFx(o);
10502             };
10503
10504             var width = this.getWidth();
10505             var height = this.getHeight();
10506
10507             arguments.callee.anim = this.fxanim({
10508                     width : {to: this.adjustWidth(width * 2)},
10509                     height : {to: this.adjustHeight(height * 2)},
10510                     points : {by: [-(width * .5), -(height * .5)]},
10511                     opacity : {to: 0},
10512                     fontSize: {to:200, unit: "%"}
10513                 },
10514                 o,
10515                 'motion',
10516                 .5,
10517                 "easeOut", after);
10518         });
10519         return this;
10520     },
10521
10522         /**
10523          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10524          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10525          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10526          * Usage:
10527          *<pre><code>
10528 // default
10529 el.switchOff();
10530
10531 // all config options shown with default values
10532 el.switchOff({
10533     easing: 'easeIn',
10534     duration: .3,
10535     remove: false,
10536     useDisplay: false
10537 });
10538 </code></pre>
10539          * @param {Object} options (optional) Object literal with any of the Fx config options
10540          * @return {Roo.Element} The Element
10541          */
10542     switchOff : function(o){
10543         var el = this.getFxEl();
10544         o = o || {};
10545
10546         el.queueFx(o, function(){
10547             this.clearOpacity();
10548             this.clip();
10549
10550             // restore values after effect
10551             var r = this.getFxRestore();
10552             var st = this.dom.style;
10553
10554             var after = function(){
10555                 if(o.useDisplay){
10556                     el.setDisplayed(false);
10557                 }else{
10558                     el.hide();
10559                 }
10560
10561                 el.clearOpacity();
10562                 el.setPositioning(r.pos);
10563                 st.width = r.width;
10564                 st.height = r.height;
10565
10566                 el.afterFx(o);
10567             };
10568
10569             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10570                 this.clearOpacity();
10571                 (function(){
10572                     this.fxanim({
10573                         height:{to:1},
10574                         points:{by:[0, this.getHeight() * .5]}
10575                     }, o, 'motion', 0.3, 'easeIn', after);
10576                 }).defer(100, this);
10577             });
10578         });
10579         return this;
10580     },
10581
10582     /**
10583      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10584      * changed using the "attr" config option) and then fading back to the original color. If no original
10585      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10586      * Usage:
10587 <pre><code>
10588 // default: highlight background to yellow
10589 el.highlight();
10590
10591 // custom: highlight foreground text to blue for 2 seconds
10592 el.highlight("0000ff", { attr: 'color', duration: 2 });
10593
10594 // common config options shown with default values
10595 el.highlight("ffff9c", {
10596     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10597     endColor: (current color) or "ffffff",
10598     easing: 'easeIn',
10599     duration: 1
10600 });
10601 </code></pre>
10602      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10603      * @param {Object} options (optional) Object literal with any of the Fx config options
10604      * @return {Roo.Element} The Element
10605      */ 
10606     highlight : function(color, o){
10607         var el = this.getFxEl();
10608         o = o || {};
10609
10610         el.queueFx(o, function(){
10611             color = color || "ffff9c";
10612             attr = o.attr || "backgroundColor";
10613
10614             this.clearOpacity();
10615             this.show();
10616
10617             var origColor = this.getColor(attr);
10618             var restoreColor = this.dom.style[attr];
10619             endColor = (o.endColor || origColor) || "ffffff";
10620
10621             var after = function(){
10622                 el.dom.style[attr] = restoreColor;
10623                 el.afterFx(o);
10624             };
10625
10626             var a = {};
10627             a[attr] = {from: color, to: endColor};
10628             arguments.callee.anim = this.fxanim(a,
10629                 o,
10630                 'color',
10631                 1,
10632                 'easeIn', after);
10633         });
10634         return this;
10635     },
10636
10637    /**
10638     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10639     * Usage:
10640 <pre><code>
10641 // default: a single light blue ripple
10642 el.frame();
10643
10644 // custom: 3 red ripples lasting 3 seconds total
10645 el.frame("ff0000", 3, { duration: 3 });
10646
10647 // common config options shown with default values
10648 el.frame("C3DAF9", 1, {
10649     duration: 1 //duration of entire animation (not each individual ripple)
10650     // Note: Easing is not configurable and will be ignored if included
10651 });
10652 </code></pre>
10653     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10654     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10655     * @param {Object} options (optional) Object literal with any of the Fx config options
10656     * @return {Roo.Element} The Element
10657     */
10658     frame : function(color, count, o){
10659         var el = this.getFxEl();
10660         o = o || {};
10661
10662         el.queueFx(o, function(){
10663             color = color || "#C3DAF9";
10664             if(color.length == 6){
10665                 color = "#" + color;
10666             }
10667             count = count || 1;
10668             duration = o.duration || 1;
10669             this.show();
10670
10671             var b = this.getBox();
10672             var animFn = function(){
10673                 var proxy = this.createProxy({
10674
10675                      style:{
10676                         visbility:"hidden",
10677                         position:"absolute",
10678                         "z-index":"35000", // yee haw
10679                         border:"0px solid " + color
10680                      }
10681                   });
10682                 var scale = Roo.isBorderBox ? 2 : 1;
10683                 proxy.animate({
10684                     top:{from:b.y, to:b.y - 20},
10685                     left:{from:b.x, to:b.x - 20},
10686                     borderWidth:{from:0, to:10},
10687                     opacity:{from:1, to:0},
10688                     height:{from:b.height, to:(b.height + (20*scale))},
10689                     width:{from:b.width, to:(b.width + (20*scale))}
10690                 }, duration, function(){
10691                     proxy.remove();
10692                 });
10693                 if(--count > 0){
10694                      animFn.defer((duration/2)*1000, this);
10695                 }else{
10696                     el.afterFx(o);
10697                 }
10698             };
10699             animFn.call(this);
10700         });
10701         return this;
10702     },
10703
10704    /**
10705     * Creates a pause before any subsequent queued effects begin.  If there are
10706     * no effects queued after the pause it will have no effect.
10707     * Usage:
10708 <pre><code>
10709 el.pause(1);
10710 </code></pre>
10711     * @param {Number} seconds The length of time to pause (in seconds)
10712     * @return {Roo.Element} The Element
10713     */
10714     pause : function(seconds){
10715         var el = this.getFxEl();
10716         var o = {};
10717
10718         el.queueFx(o, function(){
10719             setTimeout(function(){
10720                 el.afterFx(o);
10721             }, seconds * 1000);
10722         });
10723         return this;
10724     },
10725
10726    /**
10727     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10728     * using the "endOpacity" config option.
10729     * Usage:
10730 <pre><code>
10731 // default: fade in from opacity 0 to 100%
10732 el.fadeIn();
10733
10734 // custom: fade in from opacity 0 to 75% over 2 seconds
10735 el.fadeIn({ endOpacity: .75, duration: 2});
10736
10737 // common config options shown with default values
10738 el.fadeIn({
10739     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10740     easing: 'easeOut',
10741     duration: .5
10742 });
10743 </code></pre>
10744     * @param {Object} options (optional) Object literal with any of the Fx config options
10745     * @return {Roo.Element} The Element
10746     */
10747     fadeIn : function(o){
10748         var el = this.getFxEl();
10749         o = o || {};
10750         el.queueFx(o, function(){
10751             this.setOpacity(0);
10752             this.fixDisplay();
10753             this.dom.style.visibility = 'visible';
10754             var to = o.endOpacity || 1;
10755             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10756                 o, null, .5, "easeOut", function(){
10757                 if(to == 1){
10758                     this.clearOpacity();
10759                 }
10760                 el.afterFx(o);
10761             });
10762         });
10763         return this;
10764     },
10765
10766    /**
10767     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10768     * using the "endOpacity" config option.
10769     * Usage:
10770 <pre><code>
10771 // default: fade out from the element's current opacity to 0
10772 el.fadeOut();
10773
10774 // custom: fade out from the element's current opacity to 25% over 2 seconds
10775 el.fadeOut({ endOpacity: .25, duration: 2});
10776
10777 // common config options shown with default values
10778 el.fadeOut({
10779     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10780     easing: 'easeOut',
10781     duration: .5
10782     remove: false,
10783     useDisplay: false
10784 });
10785 </code></pre>
10786     * @param {Object} options (optional) Object literal with any of the Fx config options
10787     * @return {Roo.Element} The Element
10788     */
10789     fadeOut : function(o){
10790         var el = this.getFxEl();
10791         o = o || {};
10792         el.queueFx(o, function(){
10793             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10794                 o, null, .5, "easeOut", function(){
10795                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10796                      this.dom.style.display = "none";
10797                 }else{
10798                      this.dom.style.visibility = "hidden";
10799                 }
10800                 this.clearOpacity();
10801                 el.afterFx(o);
10802             });
10803         });
10804         return this;
10805     },
10806
10807    /**
10808     * Animates the transition of an element's dimensions from a starting height/width
10809     * to an ending height/width.
10810     * Usage:
10811 <pre><code>
10812 // change height and width to 100x100 pixels
10813 el.scale(100, 100);
10814
10815 // common config options shown with default values.  The height and width will default to
10816 // the element's existing values if passed as null.
10817 el.scale(
10818     [element's width],
10819     [element's height], {
10820     easing: 'easeOut',
10821     duration: .35
10822 });
10823 </code></pre>
10824     * @param {Number} width  The new width (pass undefined to keep the original width)
10825     * @param {Number} height  The new height (pass undefined to keep the original height)
10826     * @param {Object} options (optional) Object literal with any of the Fx config options
10827     * @return {Roo.Element} The Element
10828     */
10829     scale : function(w, h, o){
10830         this.shift(Roo.apply({}, o, {
10831             width: w,
10832             height: h
10833         }));
10834         return this;
10835     },
10836
10837    /**
10838     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10839     * Any of these properties not specified in the config object will not be changed.  This effect 
10840     * requires that at least one new dimension, position or opacity setting must be passed in on
10841     * the config object in order for the function to have any effect.
10842     * Usage:
10843 <pre><code>
10844 // slide the element horizontally to x position 200 while changing the height and opacity
10845 el.shift({ x: 200, height: 50, opacity: .8 });
10846
10847 // common config options shown with default values.
10848 el.shift({
10849     width: [element's width],
10850     height: [element's height],
10851     x: [element's x position],
10852     y: [element's y position],
10853     opacity: [element's opacity],
10854     easing: 'easeOut',
10855     duration: .35
10856 });
10857 </code></pre>
10858     * @param {Object} options  Object literal with any of the Fx config options
10859     * @return {Roo.Element} The Element
10860     */
10861     shift : function(o){
10862         var el = this.getFxEl();
10863         o = o || {};
10864         el.queueFx(o, function(){
10865             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10866             if(w !== undefined){
10867                 a.width = {to: this.adjustWidth(w)};
10868             }
10869             if(h !== undefined){
10870                 a.height = {to: this.adjustHeight(h)};
10871             }
10872             if(x !== undefined || y !== undefined){
10873                 a.points = {to: [
10874                     x !== undefined ? x : this.getX(),
10875                     y !== undefined ? y : this.getY()
10876                 ]};
10877             }
10878             if(op !== undefined){
10879                 a.opacity = {to: op};
10880             }
10881             if(o.xy !== undefined){
10882                 a.points = {to: o.xy};
10883             }
10884             arguments.callee.anim = this.fxanim(a,
10885                 o, 'motion', .35, "easeOut", function(){
10886                 el.afterFx(o);
10887             });
10888         });
10889         return this;
10890     },
10891
10892         /**
10893          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10894          * ending point of the effect.
10895          * Usage:
10896          *<pre><code>
10897 // default: slide the element downward while fading out
10898 el.ghost();
10899
10900 // custom: slide the element out to the right with a 2-second duration
10901 el.ghost('r', { duration: 2 });
10902
10903 // common config options shown with default values
10904 el.ghost('b', {
10905     easing: 'easeOut',
10906     duration: .5
10907     remove: false,
10908     useDisplay: false
10909 });
10910 </code></pre>
10911          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10912          * @param {Object} options (optional) Object literal with any of the Fx config options
10913          * @return {Roo.Element} The Element
10914          */
10915     ghost : function(anchor, o){
10916         var el = this.getFxEl();
10917         o = o || {};
10918
10919         el.queueFx(o, function(){
10920             anchor = anchor || "b";
10921
10922             // restore values after effect
10923             var r = this.getFxRestore();
10924             var w = this.getWidth(),
10925                 h = this.getHeight();
10926
10927             var st = this.dom.style;
10928
10929             var after = function(){
10930                 if(o.useDisplay){
10931                     el.setDisplayed(false);
10932                 }else{
10933                     el.hide();
10934                 }
10935
10936                 el.clearOpacity();
10937                 el.setPositioning(r.pos);
10938                 st.width = r.width;
10939                 st.height = r.height;
10940
10941                 el.afterFx(o);
10942             };
10943
10944             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10945             switch(anchor.toLowerCase()){
10946                 case "t":
10947                     pt.by = [0, -h];
10948                 break;
10949                 case "l":
10950                     pt.by = [-w, 0];
10951                 break;
10952                 case "r":
10953                     pt.by = [w, 0];
10954                 break;
10955                 case "b":
10956                     pt.by = [0, h];
10957                 break;
10958                 case "tl":
10959                     pt.by = [-w, -h];
10960                 break;
10961                 case "bl":
10962                     pt.by = [-w, h];
10963                 break;
10964                 case "br":
10965                     pt.by = [w, h];
10966                 break;
10967                 case "tr":
10968                     pt.by = [w, -h];
10969                 break;
10970             }
10971
10972             arguments.callee.anim = this.fxanim(a,
10973                 o,
10974                 'motion',
10975                 .5,
10976                 "easeOut", after);
10977         });
10978         return this;
10979     },
10980
10981         /**
10982          * Ensures that all effects queued after syncFx is called on the element are
10983          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10984          * @return {Roo.Element} The Element
10985          */
10986     syncFx : function(){
10987         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10988             block : false,
10989             concurrent : true,
10990             stopFx : false
10991         });
10992         return this;
10993     },
10994
10995         /**
10996          * Ensures that all effects queued after sequenceFx is called on the element are
10997          * run in sequence.  This is the opposite of {@link #syncFx}.
10998          * @return {Roo.Element} The Element
10999          */
11000     sequenceFx : function(){
11001         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11002             block : false,
11003             concurrent : false,
11004             stopFx : false
11005         });
11006         return this;
11007     },
11008
11009         /* @private */
11010     nextFx : function(){
11011         var ef = this.fxQueue[0];
11012         if(ef){
11013             ef.call(this);
11014         }
11015     },
11016
11017         /**
11018          * Returns true if the element has any effects actively running or queued, else returns false.
11019          * @return {Boolean} True if element has active effects, else false
11020          */
11021     hasActiveFx : function(){
11022         return this.fxQueue && this.fxQueue[0];
11023     },
11024
11025         /**
11026          * Stops any running effects and clears the element's internal effects queue if it contains
11027          * any additional effects that haven't started yet.
11028          * @return {Roo.Element} The Element
11029          */
11030     stopFx : function(){
11031         if(this.hasActiveFx()){
11032             var cur = this.fxQueue[0];
11033             if(cur && cur.anim && cur.anim.isAnimated()){
11034                 this.fxQueue = [cur]; // clear out others
11035                 cur.anim.stop(true);
11036             }
11037         }
11038         return this;
11039     },
11040
11041         /* @private */
11042     beforeFx : function(o){
11043         if(this.hasActiveFx() && !o.concurrent){
11044            if(o.stopFx){
11045                this.stopFx();
11046                return true;
11047            }
11048            return false;
11049         }
11050         return true;
11051     },
11052
11053         /**
11054          * Returns true if the element is currently blocking so that no other effect can be queued
11055          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11056          * used to ensure that an effect initiated by a user action runs to completion prior to the
11057          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11058          * @return {Boolean} True if blocking, else false
11059          */
11060     hasFxBlock : function(){
11061         var q = this.fxQueue;
11062         return q && q[0] && q[0].block;
11063     },
11064
11065         /* @private */
11066     queueFx : function(o, fn){
11067         if(!this.fxQueue){
11068             this.fxQueue = [];
11069         }
11070         if(!this.hasFxBlock()){
11071             Roo.applyIf(o, this.fxDefaults);
11072             if(!o.concurrent){
11073                 var run = this.beforeFx(o);
11074                 fn.block = o.block;
11075                 this.fxQueue.push(fn);
11076                 if(run){
11077                     this.nextFx();
11078                 }
11079             }else{
11080                 fn.call(this);
11081             }
11082         }
11083         return this;
11084     },
11085
11086         /* @private */
11087     fxWrap : function(pos, o, vis){
11088         var wrap;
11089         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11090             var wrapXY;
11091             if(o.fixPosition){
11092                 wrapXY = this.getXY();
11093             }
11094             var div = document.createElement("div");
11095             div.style.visibility = vis;
11096             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11097             wrap.setPositioning(pos);
11098             if(wrap.getStyle("position") == "static"){
11099                 wrap.position("relative");
11100             }
11101             this.clearPositioning('auto');
11102             wrap.clip();
11103             wrap.dom.appendChild(this.dom);
11104             if(wrapXY){
11105                 wrap.setXY(wrapXY);
11106             }
11107         }
11108         return wrap;
11109     },
11110
11111         /* @private */
11112     fxUnwrap : function(wrap, pos, o){
11113         this.clearPositioning();
11114         this.setPositioning(pos);
11115         if(!o.wrap){
11116             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11117             wrap.remove();
11118         }
11119     },
11120
11121         /* @private */
11122     getFxRestore : function(){
11123         var st = this.dom.style;
11124         return {pos: this.getPositioning(), width: st.width, height : st.height};
11125     },
11126
11127         /* @private */
11128     afterFx : function(o){
11129         if(o.afterStyle){
11130             this.applyStyles(o.afterStyle);
11131         }
11132         if(o.afterCls){
11133             this.addClass(o.afterCls);
11134         }
11135         if(o.remove === true){
11136             this.remove();
11137         }
11138         Roo.callback(o.callback, o.scope, [this]);
11139         if(!o.concurrent){
11140             this.fxQueue.shift();
11141             this.nextFx();
11142         }
11143     },
11144
11145         /* @private */
11146     getFxEl : function(){ // support for composite element fx
11147         return Roo.get(this.dom);
11148     },
11149
11150         /* @private */
11151     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11152         animType = animType || 'run';
11153         opt = opt || {};
11154         var anim = Roo.lib.Anim[animType](
11155             this.dom, args,
11156             (opt.duration || defaultDur) || .35,
11157             (opt.easing || defaultEase) || 'easeOut',
11158             function(){
11159                 Roo.callback(cb, this);
11160             },
11161             this
11162         );
11163         opt.anim = anim;
11164         return anim;
11165     }
11166 };
11167
11168 // backwords compat
11169 Roo.Fx.resize = Roo.Fx.scale;
11170
11171 //When included, Roo.Fx is automatically applied to Element so that all basic
11172 //effects are available directly via the Element API
11173 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11174  * Based on:
11175  * Ext JS Library 1.1.1
11176  * Copyright(c) 2006-2007, Ext JS, LLC.
11177  *
11178  * Originally Released Under LGPL - original licence link has changed is not relivant.
11179  *
11180  * Fork - LGPL
11181  * <script type="text/javascript">
11182  */
11183
11184
11185 /**
11186  * @class Roo.CompositeElement
11187  * Standard composite class. Creates a Roo.Element for every element in the collection.
11188  * <br><br>
11189  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11190  * actions will be performed on all the elements in this collection.</b>
11191  * <br><br>
11192  * All methods return <i>this</i> and can be chained.
11193  <pre><code>
11194  var els = Roo.select("#some-el div.some-class", true);
11195  // or select directly from an existing element
11196  var el = Roo.get('some-el');
11197  el.select('div.some-class', true);
11198
11199  els.setWidth(100); // all elements become 100 width
11200  els.hide(true); // all elements fade out and hide
11201  // or
11202  els.setWidth(100).hide(true);
11203  </code></pre>
11204  */
11205 Roo.CompositeElement = function(els){
11206     this.elements = [];
11207     this.addElements(els);
11208 };
11209 Roo.CompositeElement.prototype = {
11210     isComposite: true,
11211     addElements : function(els){
11212         if(!els) {
11213             return this;
11214         }
11215         if(typeof els == "string"){
11216             els = Roo.Element.selectorFunction(els);
11217         }
11218         var yels = this.elements;
11219         var index = yels.length-1;
11220         for(var i = 0, len = els.length; i < len; i++) {
11221                 yels[++index] = Roo.get(els[i]);
11222         }
11223         return this;
11224     },
11225
11226     /**
11227     * Clears this composite and adds the elements returned by the passed selector.
11228     * @param {String/Array} els A string CSS selector, an array of elements or an element
11229     * @return {CompositeElement} this
11230     */
11231     fill : function(els){
11232         this.elements = [];
11233         this.add(els);
11234         return this;
11235     },
11236
11237     /**
11238     * Filters this composite to only elements that match the passed selector.
11239     * @param {String} selector A string CSS selector
11240     * @param {Boolean} inverse return inverse filter (not matches)
11241     * @return {CompositeElement} this
11242     */
11243     filter : function(selector, inverse){
11244         var els = [];
11245         inverse = inverse || false;
11246         this.each(function(el){
11247             var match = inverse ? !el.is(selector) : el.is(selector);
11248             if(match){
11249                 els[els.length] = el.dom;
11250             }
11251         });
11252         this.fill(els);
11253         return this;
11254     },
11255
11256     invoke : function(fn, args){
11257         var els = this.elements;
11258         for(var i = 0, len = els.length; i < len; i++) {
11259                 Roo.Element.prototype[fn].apply(els[i], args);
11260         }
11261         return this;
11262     },
11263     /**
11264     * Adds elements to this composite.
11265     * @param {String/Array} els A string CSS selector, an array of elements or an element
11266     * @return {CompositeElement} this
11267     */
11268     add : function(els){
11269         if(typeof els == "string"){
11270             this.addElements(Roo.Element.selectorFunction(els));
11271         }else if(els.length !== undefined){
11272             this.addElements(els);
11273         }else{
11274             this.addElements([els]);
11275         }
11276         return this;
11277     },
11278     /**
11279     * Calls the passed function passing (el, this, index) for each element in this composite.
11280     * @param {Function} fn The function to call
11281     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11282     * @return {CompositeElement} this
11283     */
11284     each : function(fn, scope){
11285         var els = this.elements;
11286         for(var i = 0, len = els.length; i < len; i++){
11287             if(fn.call(scope || els[i], els[i], this, i) === false) {
11288                 break;
11289             }
11290         }
11291         return this;
11292     },
11293
11294     /**
11295      * Returns the Element object at the specified index
11296      * @param {Number} index
11297      * @return {Roo.Element}
11298      */
11299     item : function(index){
11300         return this.elements[index] || null;
11301     },
11302
11303     /**
11304      * Returns the first Element
11305      * @return {Roo.Element}
11306      */
11307     first : function(){
11308         return this.item(0);
11309     },
11310
11311     /**
11312      * Returns the last Element
11313      * @return {Roo.Element}
11314      */
11315     last : function(){
11316         return this.item(this.elements.length-1);
11317     },
11318
11319     /**
11320      * Returns the number of elements in this composite
11321      * @return Number
11322      */
11323     getCount : function(){
11324         return this.elements.length;
11325     },
11326
11327     /**
11328      * Returns true if this composite contains the passed element
11329      * @return Boolean
11330      */
11331     contains : function(el){
11332         return this.indexOf(el) !== -1;
11333     },
11334
11335     /**
11336      * Returns true if this composite contains the passed element
11337      * @return Boolean
11338      */
11339     indexOf : function(el){
11340         return this.elements.indexOf(Roo.get(el));
11341     },
11342
11343
11344     /**
11345     * Removes the specified element(s).
11346     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11347     * or an array of any of those.
11348     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11349     * @return {CompositeElement} this
11350     */
11351     removeElement : function(el, removeDom){
11352         if(el instanceof Array){
11353             for(var i = 0, len = el.length; i < len; i++){
11354                 this.removeElement(el[i]);
11355             }
11356             return this;
11357         }
11358         var index = typeof el == 'number' ? el : this.indexOf(el);
11359         if(index !== -1){
11360             if(removeDom){
11361                 var d = this.elements[index];
11362                 if(d.dom){
11363                     d.remove();
11364                 }else{
11365                     d.parentNode.removeChild(d);
11366                 }
11367             }
11368             this.elements.splice(index, 1);
11369         }
11370         return this;
11371     },
11372
11373     /**
11374     * Replaces the specified element with the passed element.
11375     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11376     * to replace.
11377     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11378     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11379     * @return {CompositeElement} this
11380     */
11381     replaceElement : function(el, replacement, domReplace){
11382         var index = typeof el == 'number' ? el : this.indexOf(el);
11383         if(index !== -1){
11384             if(domReplace){
11385                 this.elements[index].replaceWith(replacement);
11386             }else{
11387                 this.elements.splice(index, 1, Roo.get(replacement))
11388             }
11389         }
11390         return this;
11391     },
11392
11393     /**
11394      * Removes all elements.
11395      */
11396     clear : function(){
11397         this.elements = [];
11398     }
11399 };
11400 (function(){
11401     Roo.CompositeElement.createCall = function(proto, fnName){
11402         if(!proto[fnName]){
11403             proto[fnName] = function(){
11404                 return this.invoke(fnName, arguments);
11405             };
11406         }
11407     };
11408     for(var fnName in Roo.Element.prototype){
11409         if(typeof Roo.Element.prototype[fnName] == "function"){
11410             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11411         }
11412     };
11413 })();
11414 /*
11415  * Based on:
11416  * Ext JS Library 1.1.1
11417  * Copyright(c) 2006-2007, Ext JS, LLC.
11418  *
11419  * Originally Released Under LGPL - original licence link has changed is not relivant.
11420  *
11421  * Fork - LGPL
11422  * <script type="text/javascript">
11423  */
11424
11425 /**
11426  * @class Roo.CompositeElementLite
11427  * @extends Roo.CompositeElement
11428  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11429  <pre><code>
11430  var els = Roo.select("#some-el div.some-class");
11431  // or select directly from an existing element
11432  var el = Roo.get('some-el');
11433  el.select('div.some-class');
11434
11435  els.setWidth(100); // all elements become 100 width
11436  els.hide(true); // all elements fade out and hide
11437  // or
11438  els.setWidth(100).hide(true);
11439  </code></pre><br><br>
11440  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11441  * actions will be performed on all the elements in this collection.</b>
11442  */
11443 Roo.CompositeElementLite = function(els){
11444     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11445     this.el = new Roo.Element.Flyweight();
11446 };
11447 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11448     addElements : function(els){
11449         if(els){
11450             if(els instanceof Array){
11451                 this.elements = this.elements.concat(els);
11452             }else{
11453                 var yels = this.elements;
11454                 var index = yels.length-1;
11455                 for(var i = 0, len = els.length; i < len; i++) {
11456                     yels[++index] = els[i];
11457                 }
11458             }
11459         }
11460         return this;
11461     },
11462     invoke : function(fn, args){
11463         var els = this.elements;
11464         var el = this.el;
11465         for(var i = 0, len = els.length; i < len; i++) {
11466             el.dom = els[i];
11467                 Roo.Element.prototype[fn].apply(el, args);
11468         }
11469         return this;
11470     },
11471     /**
11472      * Returns a flyweight Element of the dom element object at the specified index
11473      * @param {Number} index
11474      * @return {Roo.Element}
11475      */
11476     item : function(index){
11477         if(!this.elements[index]){
11478             return null;
11479         }
11480         this.el.dom = this.elements[index];
11481         return this.el;
11482     },
11483
11484     // fixes scope with flyweight
11485     addListener : function(eventName, handler, scope, opt){
11486         var els = this.elements;
11487         for(var i = 0, len = els.length; i < len; i++) {
11488             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11489         }
11490         return this;
11491     },
11492
11493     /**
11494     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11495     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11496     * a reference to the dom node, use el.dom.</b>
11497     * @param {Function} fn The function to call
11498     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11499     * @return {CompositeElement} this
11500     */
11501     each : function(fn, scope){
11502         var els = this.elements;
11503         var el = this.el;
11504         for(var i = 0, len = els.length; i < len; i++){
11505             el.dom = els[i];
11506                 if(fn.call(scope || el, el, this, i) === false){
11507                 break;
11508             }
11509         }
11510         return this;
11511     },
11512
11513     indexOf : function(el){
11514         return this.elements.indexOf(Roo.getDom(el));
11515     },
11516
11517     replaceElement : function(el, replacement, domReplace){
11518         var index = typeof el == 'number' ? el : this.indexOf(el);
11519         if(index !== -1){
11520             replacement = Roo.getDom(replacement);
11521             if(domReplace){
11522                 var d = this.elements[index];
11523                 d.parentNode.insertBefore(replacement, d);
11524                 d.parentNode.removeChild(d);
11525             }
11526             this.elements.splice(index, 1, replacement);
11527         }
11528         return this;
11529     }
11530 });
11531 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11532
11533 /*
11534  * Based on:
11535  * Ext JS Library 1.1.1
11536  * Copyright(c) 2006-2007, Ext JS, LLC.
11537  *
11538  * Originally Released Under LGPL - original licence link has changed is not relivant.
11539  *
11540  * Fork - LGPL
11541  * <script type="text/javascript">
11542  */
11543
11544  
11545
11546 /**
11547  * @class Roo.data.Connection
11548  * @extends Roo.util.Observable
11549  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11550  * either to a configured URL, or to a URL specified at request time. 
11551  * 
11552  * Requests made by this class are asynchronous, and will return immediately. No data from
11553  * the server will be available to the statement immediately following the {@link #request} call.
11554  * To process returned data, use a callback in the request options object, or an event listener.
11555  * 
11556  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11557  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11558  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11559  * property and, if present, the IFRAME's XML document as the responseXML property.
11560  * 
11561  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11562  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11563  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11564  * standard DOM methods.
11565  * @constructor
11566  * @param {Object} config a configuration object.
11567  */
11568 Roo.data.Connection = function(config){
11569     Roo.apply(this, config);
11570     this.addEvents({
11571         /**
11572          * @event beforerequest
11573          * Fires before a network request is made to retrieve a data object.
11574          * @param {Connection} conn This Connection object.
11575          * @param {Object} options The options config object passed to the {@link #request} method.
11576          */
11577         "beforerequest" : true,
11578         /**
11579          * @event requestcomplete
11580          * Fires if the request was successfully completed.
11581          * @param {Connection} conn This Connection object.
11582          * @param {Object} response The XHR object containing the response data.
11583          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11584          * @param {Object} options The options config object passed to the {@link #request} method.
11585          */
11586         "requestcomplete" : true,
11587         /**
11588          * @event requestexception
11589          * Fires if an error HTTP status was returned from the server.
11590          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11591          * @param {Connection} conn This Connection object.
11592          * @param {Object} response The XHR object containing the response data.
11593          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11594          * @param {Object} options The options config object passed to the {@link #request} method.
11595          */
11596         "requestexception" : true
11597     });
11598     Roo.data.Connection.superclass.constructor.call(this);
11599 };
11600
11601 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11602     /**
11603      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11604      */
11605     /**
11606      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11607      * extra parameters to each request made by this object. (defaults to undefined)
11608      */
11609     /**
11610      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11611      *  to each request made by this object. (defaults to undefined)
11612      */
11613     /**
11614      * @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)
11615      */
11616     /**
11617      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11618      */
11619     timeout : 30000,
11620     /**
11621      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11622      * @type Boolean
11623      */
11624     autoAbort:false,
11625
11626     /**
11627      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11628      * @type Boolean
11629      */
11630     disableCaching: true,
11631
11632     /**
11633      * Sends an HTTP request to a remote server.
11634      * @param {Object} options An object which may contain the following properties:<ul>
11635      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11636      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11637      * request, a url encoded string or a function to call to get either.</li>
11638      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11639      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11640      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11641      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11642      * <li>options {Object} The parameter to the request call.</li>
11643      * <li>success {Boolean} True if the request succeeded.</li>
11644      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11645      * </ul></li>
11646      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11647      * The callback is passed the following parameters:<ul>
11648      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11649      * <li>options {Object} The parameter to the request call.</li>
11650      * </ul></li>
11651      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11652      * The callback is passed the following parameters:<ul>
11653      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11654      * <li>options {Object} The parameter to the request call.</li>
11655      * </ul></li>
11656      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11657      * for the callback function. Defaults to the browser window.</li>
11658      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11659      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11660      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11661      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11662      * params for the post data. Any params will be appended to the URL.</li>
11663      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11664      * </ul>
11665      * @return {Number} transactionId
11666      */
11667     request : function(o){
11668         if(this.fireEvent("beforerequest", this, o) !== false){
11669             var p = o.params;
11670
11671             if(typeof p == "function"){
11672                 p = p.call(o.scope||window, o);
11673             }
11674             if(typeof p == "object"){
11675                 p = Roo.urlEncode(o.params);
11676             }
11677             if(this.extraParams){
11678                 var extras = Roo.urlEncode(this.extraParams);
11679                 p = p ? (p + '&' + extras) : extras;
11680             }
11681
11682             var url = o.url || this.url;
11683             if(typeof url == 'function'){
11684                 url = url.call(o.scope||window, o);
11685             }
11686
11687             if(o.form){
11688                 var form = Roo.getDom(o.form);
11689                 url = url || form.action;
11690
11691                 var enctype = form.getAttribute("enctype");
11692                 
11693                 if (o.formData) {
11694                     return this.doFormDataUpload(o, url);
11695                 }
11696                 
11697                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11698                     return this.doFormUpload(o, p, url);
11699                 }
11700                 var f = Roo.lib.Ajax.serializeForm(form);
11701                 p = p ? (p + '&' + f) : f;
11702             }
11703             
11704             if (!o.form && o.formData) {
11705                 o.formData = o.formData === true ? new FormData() : o.formData;
11706                 for (var k in o.params) {
11707                     o.formData.append(k,o.params[k]);
11708                 }
11709                     
11710                 return this.doFormDataUpload(o, url);
11711             }
11712             
11713
11714             var hs = o.headers;
11715             if(this.defaultHeaders){
11716                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11717                 if(!o.headers){
11718                     o.headers = hs;
11719                 }
11720             }
11721
11722             var cb = {
11723                 success: this.handleResponse,
11724                 failure: this.handleFailure,
11725                 scope: this,
11726                 argument: {options: o},
11727                 timeout : o.timeout || this.timeout
11728             };
11729
11730             var method = o.method||this.method||(p ? "POST" : "GET");
11731
11732             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11733                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11734             }
11735
11736             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11737                 if(o.autoAbort){
11738                     this.abort();
11739                 }
11740             }else if(this.autoAbort !== false){
11741                 this.abort();
11742             }
11743
11744             if((method == 'GET' && p) || o.xmlData){
11745                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11746                 p = '';
11747             }
11748             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11749             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11750             Roo.lib.Ajax.useDefaultHeader == true;
11751             return this.transId;
11752         }else{
11753             Roo.callback(o.callback, o.scope, [o, null, null]);
11754             return null;
11755         }
11756     },
11757
11758     /**
11759      * Determine whether this object has a request outstanding.
11760      * @param {Number} transactionId (Optional) defaults to the last transaction
11761      * @return {Boolean} True if there is an outstanding request.
11762      */
11763     isLoading : function(transId){
11764         if(transId){
11765             return Roo.lib.Ajax.isCallInProgress(transId);
11766         }else{
11767             return this.transId ? true : false;
11768         }
11769     },
11770
11771     /**
11772      * Aborts any outstanding request.
11773      * @param {Number} transactionId (Optional) defaults to the last transaction
11774      */
11775     abort : function(transId){
11776         if(transId || this.isLoading()){
11777             Roo.lib.Ajax.abort(transId || this.transId);
11778         }
11779     },
11780
11781     // private
11782     handleResponse : function(response){
11783         this.transId = false;
11784         var options = response.argument.options;
11785         response.argument = options ? options.argument : null;
11786         this.fireEvent("requestcomplete", this, response, options);
11787         Roo.callback(options.success, options.scope, [response, options]);
11788         Roo.callback(options.callback, options.scope, [options, true, response]);
11789     },
11790
11791     // private
11792     handleFailure : function(response, e){
11793         this.transId = false;
11794         var options = response.argument.options;
11795         response.argument = options ? options.argument : null;
11796         this.fireEvent("requestexception", this, response, options, e);
11797         Roo.callback(options.failure, options.scope, [response, options]);
11798         Roo.callback(options.callback, options.scope, [options, false, response]);
11799     },
11800
11801     // private
11802     doFormUpload : function(o, ps, url){
11803         var id = Roo.id();
11804         var frame = document.createElement('iframe');
11805         frame.id = id;
11806         frame.name = id;
11807         frame.className = 'x-hidden';
11808         if(Roo.isIE){
11809             frame.src = Roo.SSL_SECURE_URL;
11810         }
11811         document.body.appendChild(frame);
11812
11813         if(Roo.isIE){
11814            document.frames[id].name = id;
11815         }
11816
11817         var form = Roo.getDom(o.form);
11818         form.target = id;
11819         form.method = 'POST';
11820         form.enctype = form.encoding = 'multipart/form-data';
11821         if(url){
11822             form.action = url;
11823         }
11824
11825         var hiddens, hd;
11826         if(ps){ // add dynamic params
11827             hiddens = [];
11828             ps = Roo.urlDecode(ps, false);
11829             for(var k in ps){
11830                 if(ps.hasOwnProperty(k)){
11831                     hd = document.createElement('input');
11832                     hd.type = 'hidden';
11833                     hd.name = k;
11834                     hd.value = ps[k];
11835                     form.appendChild(hd);
11836                     hiddens.push(hd);
11837                 }
11838             }
11839         }
11840
11841         function cb(){
11842             var r = {  // bogus response object
11843                 responseText : '',
11844                 responseXML : null
11845             };
11846
11847             r.argument = o ? o.argument : null;
11848
11849             try { //
11850                 var doc;
11851                 if(Roo.isIE){
11852                     doc = frame.contentWindow.document;
11853                 }else {
11854                     doc = (frame.contentDocument || window.frames[id].document);
11855                 }
11856                 if(doc && doc.body){
11857                     r.responseText = doc.body.innerHTML;
11858                 }
11859                 if(doc && doc.XMLDocument){
11860                     r.responseXML = doc.XMLDocument;
11861                 }else {
11862                     r.responseXML = doc;
11863                 }
11864             }
11865             catch(e) {
11866                 // ignore
11867             }
11868
11869             Roo.EventManager.removeListener(frame, 'load', cb, this);
11870
11871             this.fireEvent("requestcomplete", this, r, o);
11872             Roo.callback(o.success, o.scope, [r, o]);
11873             Roo.callback(o.callback, o.scope, [o, true, r]);
11874
11875             setTimeout(function(){document.body.removeChild(frame);}, 100);
11876         }
11877
11878         Roo.EventManager.on(frame, 'load', cb, this);
11879         form.submit();
11880
11881         if(hiddens){ // remove dynamic params
11882             for(var i = 0, len = hiddens.length; i < len; i++){
11883                 form.removeChild(hiddens[i]);
11884             }
11885         }
11886     },
11887     // this is a 'formdata version???'
11888     
11889     
11890     doFormDataUpload : function(o,  url)
11891     {
11892         var formData;
11893         if (o.form) {
11894             var form =  Roo.getDom(o.form);
11895             form.enctype = form.encoding = 'multipart/form-data';
11896             formData = o.formData === true ? new FormData(form) : o.formData;
11897         } else {
11898             formData = o.formData === true ? new FormData() : o.formData;
11899         }
11900         
11901       
11902         var cb = {
11903             success: this.handleResponse,
11904             failure: this.handleFailure,
11905             scope: this,
11906             argument: {options: o},
11907             timeout : o.timeout || this.timeout
11908         };
11909  
11910         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11911             if(o.autoAbort){
11912                 this.abort();
11913             }
11914         }else if(this.autoAbort !== false){
11915             this.abort();
11916         }
11917
11918         //Roo.lib.Ajax.defaultPostHeader = null;
11919         Roo.lib.Ajax.useDefaultHeader = false;
11920         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
11921         Roo.lib.Ajax.useDefaultHeader = true;
11922  
11923          
11924     }
11925     
11926 });
11927 /*
11928  * Based on:
11929  * Ext JS Library 1.1.1
11930  * Copyright(c) 2006-2007, Ext JS, LLC.
11931  *
11932  * Originally Released Under LGPL - original licence link has changed is not relivant.
11933  *
11934  * Fork - LGPL
11935  * <script type="text/javascript">
11936  */
11937  
11938 /**
11939  * Global Ajax request class.
11940  * 
11941  * @class Roo.Ajax
11942  * @extends Roo.data.Connection
11943  * @static
11944  * 
11945  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11946  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11947  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11948  * @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)
11949  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11950  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11951  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11952  */
11953 Roo.Ajax = new Roo.data.Connection({
11954     // fix up the docs
11955     /**
11956      * @scope Roo.Ajax
11957      * @type {Boolear} 
11958      */
11959     autoAbort : false,
11960
11961     /**
11962      * Serialize the passed form into a url encoded string
11963      * @scope Roo.Ajax
11964      * @param {String/HTMLElement} form
11965      * @return {String}
11966      */
11967     serializeForm : function(form){
11968         return Roo.lib.Ajax.serializeForm(form);
11969     }
11970 });/*
11971  * Based on:
11972  * Ext JS Library 1.1.1
11973  * Copyright(c) 2006-2007, Ext JS, LLC.
11974  *
11975  * Originally Released Under LGPL - original licence link has changed is not relivant.
11976  *
11977  * Fork - LGPL
11978  * <script type="text/javascript">
11979  */
11980
11981  
11982 /**
11983  * @class Roo.UpdateManager
11984  * @extends Roo.util.Observable
11985  * Provides AJAX-style update for Element object.<br><br>
11986  * Usage:<br>
11987  * <pre><code>
11988  * // Get it from a Roo.Element object
11989  * var el = Roo.get("foo");
11990  * var mgr = el.getUpdateManager();
11991  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11992  * ...
11993  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11994  * <br>
11995  * // or directly (returns the same UpdateManager instance)
11996  * var mgr = new Roo.UpdateManager("myElementId");
11997  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11998  * mgr.on("update", myFcnNeedsToKnow);
11999  * <br>
12000    // short handed call directly from the element object
12001    Roo.get("foo").load({
12002         url: "bar.php",
12003         scripts:true,
12004         params: "for=bar",
12005         text: "Loading Foo..."
12006    });
12007  * </code></pre>
12008  * @constructor
12009  * Create new UpdateManager directly.
12010  * @param {String/HTMLElement/Roo.Element} el The element to update
12011  * @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).
12012  */
12013 Roo.UpdateManager = function(el, forceNew){
12014     el = Roo.get(el);
12015     if(!forceNew && el.updateManager){
12016         return el.updateManager;
12017     }
12018     /**
12019      * The Element object
12020      * @type Roo.Element
12021      */
12022     this.el = el;
12023     /**
12024      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12025      * @type String
12026      */
12027     this.defaultUrl = null;
12028
12029     this.addEvents({
12030         /**
12031          * @event beforeupdate
12032          * Fired before an update is made, return false from your handler and the update is cancelled.
12033          * @param {Roo.Element} el
12034          * @param {String/Object/Function} url
12035          * @param {String/Object} params
12036          */
12037         "beforeupdate": true,
12038         /**
12039          * @event update
12040          * Fired after successful update is made.
12041          * @param {Roo.Element} el
12042          * @param {Object} oResponseObject The response Object
12043          */
12044         "update": true,
12045         /**
12046          * @event failure
12047          * Fired on update failure.
12048          * @param {Roo.Element} el
12049          * @param {Object} oResponseObject The response Object
12050          */
12051         "failure": true
12052     });
12053     var d = Roo.UpdateManager.defaults;
12054     /**
12055      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12056      * @type String
12057      */
12058     this.sslBlankUrl = d.sslBlankUrl;
12059     /**
12060      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12061      * @type Boolean
12062      */
12063     this.disableCaching = d.disableCaching;
12064     /**
12065      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12066      * @type String
12067      */
12068     this.indicatorText = d.indicatorText;
12069     /**
12070      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12071      * @type String
12072      */
12073     this.showLoadIndicator = d.showLoadIndicator;
12074     /**
12075      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12076      * @type Number
12077      */
12078     this.timeout = d.timeout;
12079
12080     /**
12081      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12082      * @type Boolean
12083      */
12084     this.loadScripts = d.loadScripts;
12085
12086     /**
12087      * Transaction object of current executing transaction
12088      */
12089     this.transaction = null;
12090
12091     /**
12092      * @private
12093      */
12094     this.autoRefreshProcId = null;
12095     /**
12096      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12097      * @type Function
12098      */
12099     this.refreshDelegate = this.refresh.createDelegate(this);
12100     /**
12101      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12102      * @type Function
12103      */
12104     this.updateDelegate = this.update.createDelegate(this);
12105     /**
12106      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12107      * @type Function
12108      */
12109     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12110     /**
12111      * @private
12112      */
12113     this.successDelegate = this.processSuccess.createDelegate(this);
12114     /**
12115      * @private
12116      */
12117     this.failureDelegate = this.processFailure.createDelegate(this);
12118
12119     if(!this.renderer){
12120      /**
12121       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12122       */
12123     this.renderer = new Roo.UpdateManager.BasicRenderer();
12124     }
12125     
12126     Roo.UpdateManager.superclass.constructor.call(this);
12127 };
12128
12129 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12130     /**
12131      * Get the Element this UpdateManager is bound to
12132      * @return {Roo.Element} The element
12133      */
12134     getEl : function(){
12135         return this.el;
12136     },
12137     /**
12138      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12139      * @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:
12140 <pre><code>
12141 um.update({<br/>
12142     url: "your-url.php",<br/>
12143     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12144     callback: yourFunction,<br/>
12145     scope: yourObject, //(optional scope)  <br/>
12146     discardUrl: false, <br/>
12147     nocache: false,<br/>
12148     text: "Loading...",<br/>
12149     timeout: 30,<br/>
12150     scripts: false<br/>
12151 });
12152 </code></pre>
12153      * The only required property is url. The optional properties nocache, text and scripts
12154      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12155      * @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}
12156      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12157      * @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.
12158      */
12159     update : function(url, params, callback, discardUrl){
12160         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12161             var method = this.method,
12162                 cfg;
12163             if(typeof url == "object"){ // must be config object
12164                 cfg = url;
12165                 url = cfg.url;
12166                 params = params || cfg.params;
12167                 callback = callback || cfg.callback;
12168                 discardUrl = discardUrl || cfg.discardUrl;
12169                 if(callback && cfg.scope){
12170                     callback = callback.createDelegate(cfg.scope);
12171                 }
12172                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12173                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12174                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12175                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12176                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12177             }
12178             this.showLoading();
12179             if(!discardUrl){
12180                 this.defaultUrl = url;
12181             }
12182             if(typeof url == "function"){
12183                 url = url.call(this);
12184             }
12185
12186             method = method || (params ? "POST" : "GET");
12187             if(method == "GET"){
12188                 url = this.prepareUrl(url);
12189             }
12190
12191             var o = Roo.apply(cfg ||{}, {
12192                 url : url,
12193                 params: params,
12194                 success: this.successDelegate,
12195                 failure: this.failureDelegate,
12196                 callback: undefined,
12197                 timeout: (this.timeout*1000),
12198                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12199             });
12200             Roo.log("updated manager called with timeout of " + o.timeout);
12201             this.transaction = Roo.Ajax.request(o);
12202         }
12203     },
12204
12205     /**
12206      * 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.
12207      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12208      * @param {String/HTMLElement} form The form Id or form element
12209      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12210      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12211      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12212      */
12213     formUpdate : function(form, url, reset, callback){
12214         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12215             if(typeof url == "function"){
12216                 url = url.call(this);
12217             }
12218             form = Roo.getDom(form);
12219             this.transaction = Roo.Ajax.request({
12220                 form: form,
12221                 url:url,
12222                 success: this.successDelegate,
12223                 failure: this.failureDelegate,
12224                 timeout: (this.timeout*1000),
12225                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12226             });
12227             this.showLoading.defer(1, this);
12228         }
12229     },
12230
12231     /**
12232      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12233      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12234      */
12235     refresh : function(callback){
12236         if(this.defaultUrl == null){
12237             return;
12238         }
12239         this.update(this.defaultUrl, null, callback, true);
12240     },
12241
12242     /**
12243      * Set this element to auto refresh.
12244      * @param {Number} interval How often to update (in seconds).
12245      * @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)
12246      * @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}
12247      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12248      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12249      */
12250     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12251         if(refreshNow){
12252             this.update(url || this.defaultUrl, params, callback, true);
12253         }
12254         if(this.autoRefreshProcId){
12255             clearInterval(this.autoRefreshProcId);
12256         }
12257         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12258     },
12259
12260     /**
12261      * Stop auto refresh on this element.
12262      */
12263      stopAutoRefresh : function(){
12264         if(this.autoRefreshProcId){
12265             clearInterval(this.autoRefreshProcId);
12266             delete this.autoRefreshProcId;
12267         }
12268     },
12269
12270     isAutoRefreshing : function(){
12271        return this.autoRefreshProcId ? true : false;
12272     },
12273     /**
12274      * Called to update the element to "Loading" state. Override to perform custom action.
12275      */
12276     showLoading : function(){
12277         if(this.showLoadIndicator){
12278             this.el.update(this.indicatorText);
12279         }
12280     },
12281
12282     /**
12283      * Adds unique parameter to query string if disableCaching = true
12284      * @private
12285      */
12286     prepareUrl : function(url){
12287         if(this.disableCaching){
12288             var append = "_dc=" + (new Date().getTime());
12289             if(url.indexOf("?") !== -1){
12290                 url += "&" + append;
12291             }else{
12292                 url += "?" + append;
12293             }
12294         }
12295         return url;
12296     },
12297
12298     /**
12299      * @private
12300      */
12301     processSuccess : function(response){
12302         this.transaction = null;
12303         if(response.argument.form && response.argument.reset){
12304             try{ // put in try/catch since some older FF releases had problems with this
12305                 response.argument.form.reset();
12306             }catch(e){}
12307         }
12308         if(this.loadScripts){
12309             this.renderer.render(this.el, response, this,
12310                 this.updateComplete.createDelegate(this, [response]));
12311         }else{
12312             this.renderer.render(this.el, response, this);
12313             this.updateComplete(response);
12314         }
12315     },
12316
12317     updateComplete : function(response){
12318         this.fireEvent("update", this.el, response);
12319         if(typeof response.argument.callback == "function"){
12320             response.argument.callback(this.el, true, response);
12321         }
12322     },
12323
12324     /**
12325      * @private
12326      */
12327     processFailure : function(response){
12328         this.transaction = null;
12329         this.fireEvent("failure", this.el, response);
12330         if(typeof response.argument.callback == "function"){
12331             response.argument.callback(this.el, false, response);
12332         }
12333     },
12334
12335     /**
12336      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12337      * @param {Object} renderer The object implementing the render() method
12338      */
12339     setRenderer : function(renderer){
12340         this.renderer = renderer;
12341     },
12342
12343     getRenderer : function(){
12344        return this.renderer;
12345     },
12346
12347     /**
12348      * Set the defaultUrl used for updates
12349      * @param {String/Function} defaultUrl The url or a function to call to get the url
12350      */
12351     setDefaultUrl : function(defaultUrl){
12352         this.defaultUrl = defaultUrl;
12353     },
12354
12355     /**
12356      * Aborts the executing transaction
12357      */
12358     abort : function(){
12359         if(this.transaction){
12360             Roo.Ajax.abort(this.transaction);
12361         }
12362     },
12363
12364     /**
12365      * Returns true if an update is in progress
12366      * @return {Boolean}
12367      */
12368     isUpdating : function(){
12369         if(this.transaction){
12370             return Roo.Ajax.isLoading(this.transaction);
12371         }
12372         return false;
12373     }
12374 });
12375
12376 /**
12377  * @class Roo.UpdateManager.defaults
12378  * @static (not really - but it helps the doc tool)
12379  * The defaults collection enables customizing the default properties of UpdateManager
12380  */
12381    Roo.UpdateManager.defaults = {
12382        /**
12383          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12384          * @type Number
12385          */
12386          timeout : 30,
12387
12388          /**
12389          * True to process scripts by default (Defaults to false).
12390          * @type Boolean
12391          */
12392         loadScripts : false,
12393
12394         /**
12395         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12396         * @type String
12397         */
12398         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12399         /**
12400          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12401          * @type Boolean
12402          */
12403         disableCaching : false,
12404         /**
12405          * Whether to show indicatorText when loading (Defaults to true).
12406          * @type Boolean
12407          */
12408         showLoadIndicator : true,
12409         /**
12410          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12411          * @type String
12412          */
12413         indicatorText : '<div class="loading-indicator">Loading...</div>'
12414    };
12415
12416 /**
12417  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12418  *Usage:
12419  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12420  * @param {String/HTMLElement/Roo.Element} el The element to update
12421  * @param {String} url The url
12422  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12423  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12424  * @static
12425  * @deprecated
12426  * @member Roo.UpdateManager
12427  */
12428 Roo.UpdateManager.updateElement = function(el, url, params, options){
12429     var um = Roo.get(el, true).getUpdateManager();
12430     Roo.apply(um, options);
12431     um.update(url, params, options ? options.callback : null);
12432 };
12433 // alias for backwards compat
12434 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12435 /**
12436  * @class Roo.UpdateManager.BasicRenderer
12437  * Default Content renderer. Updates the elements innerHTML with the responseText.
12438  */
12439 Roo.UpdateManager.BasicRenderer = function(){};
12440
12441 Roo.UpdateManager.BasicRenderer.prototype = {
12442     /**
12443      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12444      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12445      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12446      * @param {Roo.Element} el The element being rendered
12447      * @param {Object} response The YUI Connect response object
12448      * @param {UpdateManager} updateManager The calling update manager
12449      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12450      */
12451      render : function(el, response, updateManager, callback){
12452         el.update(response.responseText, updateManager.loadScripts, callback);
12453     }
12454 };
12455 /*
12456  * Based on:
12457  * Roo JS
12458  * (c)) Alan Knowles
12459  * Licence : LGPL
12460  */
12461
12462
12463 /**
12464  * @class Roo.DomTemplate
12465  * @extends Roo.Template
12466  * An effort at a dom based template engine..
12467  *
12468  * Similar to XTemplate, except it uses dom parsing to create the template..
12469  *
12470  * Supported features:
12471  *
12472  *  Tags:
12473
12474 <pre><code>
12475       {a_variable} - output encoded.
12476       {a_variable.format:("Y-m-d")} - call a method on the variable
12477       {a_variable:raw} - unencoded output
12478       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12479       {a_variable:this.method_on_template(...)} - call a method on the template object.
12480  
12481 </code></pre>
12482  *  The tpl tag:
12483 <pre><code>
12484         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12485         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12486         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12487         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12488   
12489 </code></pre>
12490  *      
12491  */
12492 Roo.DomTemplate = function()
12493 {
12494      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12495      if (this.html) {
12496         this.compile();
12497      }
12498 };
12499
12500
12501 Roo.extend(Roo.DomTemplate, Roo.Template, {
12502     /**
12503      * id counter for sub templates.
12504      */
12505     id : 0,
12506     /**
12507      * flag to indicate if dom parser is inside a pre,
12508      * it will strip whitespace if not.
12509      */
12510     inPre : false,
12511     
12512     /**
12513      * The various sub templates
12514      */
12515     tpls : false,
12516     
12517     
12518     
12519     /**
12520      *
12521      * basic tag replacing syntax
12522      * WORD:WORD()
12523      *
12524      * // you can fake an object call by doing this
12525      *  x.t:(test,tesT) 
12526      * 
12527      */
12528     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12529     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12530     
12531     iterChild : function (node, method) {
12532         
12533         var oldPre = this.inPre;
12534         if (node.tagName == 'PRE') {
12535             this.inPre = true;
12536         }
12537         for( var i = 0; i < node.childNodes.length; i++) {
12538             method.call(this, node.childNodes[i]);
12539         }
12540         this.inPre = oldPre;
12541     },
12542     
12543     
12544     
12545     /**
12546      * compile the template
12547      *
12548      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12549      *
12550      */
12551     compile: function()
12552     {
12553         var s = this.html;
12554         
12555         // covert the html into DOM...
12556         var doc = false;
12557         var div =false;
12558         try {
12559             doc = document.implementation.createHTMLDocument("");
12560             doc.documentElement.innerHTML =   this.html  ;
12561             div = doc.documentElement;
12562         } catch (e) {
12563             // old IE... - nasty -- it causes all sorts of issues.. with
12564             // images getting pulled from server..
12565             div = document.createElement('div');
12566             div.innerHTML = this.html;
12567         }
12568         //doc.documentElement.innerHTML = htmlBody
12569          
12570         
12571         
12572         this.tpls = [];
12573         var _t = this;
12574         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12575         
12576         var tpls = this.tpls;
12577         
12578         // create a top level template from the snippet..
12579         
12580         //Roo.log(div.innerHTML);
12581         
12582         var tpl = {
12583             uid : 'master',
12584             id : this.id++,
12585             attr : false,
12586             value : false,
12587             body : div.innerHTML,
12588             
12589             forCall : false,
12590             execCall : false,
12591             dom : div,
12592             isTop : true
12593             
12594         };
12595         tpls.unshift(tpl);
12596         
12597         
12598         // compile them...
12599         this.tpls = [];
12600         Roo.each(tpls, function(tp){
12601             this.compileTpl(tp);
12602             this.tpls[tp.id] = tp;
12603         }, this);
12604         
12605         this.master = tpls[0];
12606         return this;
12607         
12608         
12609     },
12610     
12611     compileNode : function(node, istop) {
12612         // test for
12613         //Roo.log(node);
12614         
12615         
12616         // skip anything not a tag..
12617         if (node.nodeType != 1) {
12618             if (node.nodeType == 3 && !this.inPre) {
12619                 // reduce white space..
12620                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12621                 
12622             }
12623             return;
12624         }
12625         
12626         var tpl = {
12627             uid : false,
12628             id : false,
12629             attr : false,
12630             value : false,
12631             body : '',
12632             
12633             forCall : false,
12634             execCall : false,
12635             dom : false,
12636             isTop : istop
12637             
12638             
12639         };
12640         
12641         
12642         switch(true) {
12643             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12644             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12645             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12646             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12647             // no default..
12648         }
12649         
12650         
12651         if (!tpl.attr) {
12652             // just itterate children..
12653             this.iterChild(node,this.compileNode);
12654             return;
12655         }
12656         tpl.uid = this.id++;
12657         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12658         node.removeAttribute('roo-'+ tpl.attr);
12659         if (tpl.attr != 'name') {
12660             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12661             node.parentNode.replaceChild(placeholder,  node);
12662         } else {
12663             
12664             var placeholder =  document.createElement('span');
12665             placeholder.className = 'roo-tpl-' + tpl.value;
12666             node.parentNode.replaceChild(placeholder,  node);
12667         }
12668         
12669         // parent now sees '{domtplXXXX}
12670         this.iterChild(node,this.compileNode);
12671         
12672         // we should now have node body...
12673         var div = document.createElement('div');
12674         div.appendChild(node);
12675         tpl.dom = node;
12676         // this has the unfortunate side effect of converting tagged attributes
12677         // eg. href="{...}" into %7C...%7D
12678         // this has been fixed by searching for those combo's although it's a bit hacky..
12679         
12680         
12681         tpl.body = div.innerHTML;
12682         
12683         
12684          
12685         tpl.id = tpl.uid;
12686         switch(tpl.attr) {
12687             case 'for' :
12688                 switch (tpl.value) {
12689                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12690                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12691                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12692                 }
12693                 break;
12694             
12695             case 'exec':
12696                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12697                 break;
12698             
12699             case 'if':     
12700                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12701                 break;
12702             
12703             case 'name':
12704                 tpl.id  = tpl.value; // replace non characters???
12705                 break;
12706             
12707         }
12708         
12709         
12710         this.tpls.push(tpl);
12711         
12712         
12713         
12714     },
12715     
12716     
12717     
12718     
12719     /**
12720      * Compile a segment of the template into a 'sub-template'
12721      *
12722      * 
12723      * 
12724      *
12725      */
12726     compileTpl : function(tpl)
12727     {
12728         var fm = Roo.util.Format;
12729         var useF = this.disableFormats !== true;
12730         
12731         var sep = Roo.isGecko ? "+\n" : ",\n";
12732         
12733         var undef = function(str) {
12734             Roo.debug && Roo.log("Property not found :"  + str);
12735             return '';
12736         };
12737           
12738         //Roo.log(tpl.body);
12739         
12740         
12741         
12742         var fn = function(m, lbrace, name, format, args)
12743         {
12744             //Roo.log("ARGS");
12745             //Roo.log(arguments);
12746             args = args ? args.replace(/\\'/g,"'") : args;
12747             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12748             if (typeof(format) == 'undefined') {
12749                 format =  'htmlEncode'; 
12750             }
12751             if (format == 'raw' ) {
12752                 format = false;
12753             }
12754             
12755             if(name.substr(0, 6) == 'domtpl'){
12756                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12757             }
12758             
12759             // build an array of options to determine if value is undefined..
12760             
12761             // basically get 'xxxx.yyyy' then do
12762             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12763             //    (function () { Roo.log("Property not found"); return ''; })() :
12764             //    ......
12765             
12766             var udef_ar = [];
12767             var lookfor = '';
12768             Roo.each(name.split('.'), function(st) {
12769                 lookfor += (lookfor.length ? '.': '') + st;
12770                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12771             });
12772             
12773             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12774             
12775             
12776             if(format && useF){
12777                 
12778                 args = args ? ',' + args : "";
12779                  
12780                 if(format.substr(0, 5) != "this."){
12781                     format = "fm." + format + '(';
12782                 }else{
12783                     format = 'this.call("'+ format.substr(5) + '", ';
12784                     args = ", values";
12785                 }
12786                 
12787                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12788             }
12789              
12790             if (args && args.length) {
12791                 // called with xxyx.yuu:(test,test)
12792                 // change to ()
12793                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12794             }
12795             // raw.. - :raw modifier..
12796             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12797             
12798         };
12799         var body;
12800         // branched to use + in gecko and [].join() in others
12801         if(Roo.isGecko){
12802             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12803                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12804                     "';};};";
12805         }else{
12806             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12807             body.push(tpl.body.replace(/(\r\n|\n)/g,
12808                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12809             body.push("'].join('');};};");
12810             body = body.join('');
12811         }
12812         
12813         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12814        
12815         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12816         eval(body);
12817         
12818         return this;
12819     },
12820      
12821     /**
12822      * same as applyTemplate, except it's done to one of the subTemplates
12823      * when using named templates, you can do:
12824      *
12825      * var str = pl.applySubTemplate('your-name', values);
12826      *
12827      * 
12828      * @param {Number} id of the template
12829      * @param {Object} values to apply to template
12830      * @param {Object} parent (normaly the instance of this object)
12831      */
12832     applySubTemplate : function(id, values, parent)
12833     {
12834         
12835         
12836         var t = this.tpls[id];
12837         
12838         
12839         try { 
12840             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12841                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12842                 return '';
12843             }
12844         } catch(e) {
12845             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12846             Roo.log(values);
12847           
12848             return '';
12849         }
12850         try { 
12851             
12852             if(t.execCall && t.execCall.call(this, values, parent)){
12853                 return '';
12854             }
12855         } catch(e) {
12856             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12857             Roo.log(values);
12858             return '';
12859         }
12860         
12861         try {
12862             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12863             parent = t.target ? values : parent;
12864             if(t.forCall && vs instanceof Array){
12865                 var buf = [];
12866                 for(var i = 0, len = vs.length; i < len; i++){
12867                     try {
12868                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12869                     } catch (e) {
12870                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12871                         Roo.log(e.body);
12872                         //Roo.log(t.compiled);
12873                         Roo.log(vs[i]);
12874                     }   
12875                 }
12876                 return buf.join('');
12877             }
12878         } catch (e) {
12879             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12880             Roo.log(values);
12881             return '';
12882         }
12883         try {
12884             return t.compiled.call(this, vs, parent);
12885         } catch (e) {
12886             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12887             Roo.log(e.body);
12888             //Roo.log(t.compiled);
12889             Roo.log(values);
12890             return '';
12891         }
12892     },
12893
12894    
12895
12896     applyTemplate : function(values){
12897         return this.master.compiled.call(this, values, {});
12898         //var s = this.subs;
12899     },
12900
12901     apply : function(){
12902         return this.applyTemplate.apply(this, arguments);
12903     }
12904
12905  });
12906
12907 Roo.DomTemplate.from = function(el){
12908     el = Roo.getDom(el);
12909     return new Roo.Domtemplate(el.value || el.innerHTML);
12910 };/*
12911  * Based on:
12912  * Ext JS Library 1.1.1
12913  * Copyright(c) 2006-2007, Ext JS, LLC.
12914  *
12915  * Originally Released Under LGPL - original licence link has changed is not relivant.
12916  *
12917  * Fork - LGPL
12918  * <script type="text/javascript">
12919  */
12920
12921 /**
12922  * @class Roo.util.DelayedTask
12923  * Provides a convenient method of performing setTimeout where a new
12924  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12925  * You can use this class to buffer
12926  * the keypress events for a certain number of milliseconds, and perform only if they stop
12927  * for that amount of time.
12928  * @constructor The parameters to this constructor serve as defaults and are not required.
12929  * @param {Function} fn (optional) The default function to timeout
12930  * @param {Object} scope (optional) The default scope of that timeout
12931  * @param {Array} args (optional) The default Array of arguments
12932  */
12933 Roo.util.DelayedTask = function(fn, scope, args){
12934     var id = null, d, t;
12935
12936     var call = function(){
12937         var now = new Date().getTime();
12938         if(now - t >= d){
12939             clearInterval(id);
12940             id = null;
12941             fn.apply(scope, args || []);
12942         }
12943     };
12944     /**
12945      * Cancels any pending timeout and queues a new one
12946      * @param {Number} delay The milliseconds to delay
12947      * @param {Function} newFn (optional) Overrides function passed to constructor
12948      * @param {Object} newScope (optional) Overrides scope passed to constructor
12949      * @param {Array} newArgs (optional) Overrides args passed to constructor
12950      */
12951     this.delay = function(delay, newFn, newScope, newArgs){
12952         if(id && delay != d){
12953             this.cancel();
12954         }
12955         d = delay;
12956         t = new Date().getTime();
12957         fn = newFn || fn;
12958         scope = newScope || scope;
12959         args = newArgs || args;
12960         if(!id){
12961             id = setInterval(call, d);
12962         }
12963     };
12964
12965     /**
12966      * Cancel the last queued timeout
12967      */
12968     this.cancel = function(){
12969         if(id){
12970             clearInterval(id);
12971             id = null;
12972         }
12973     };
12974 };/*
12975  * Based on:
12976  * Ext JS Library 1.1.1
12977  * Copyright(c) 2006-2007, Ext JS, LLC.
12978  *
12979  * Originally Released Under LGPL - original licence link has changed is not relivant.
12980  *
12981  * Fork - LGPL
12982  * <script type="text/javascript">
12983  */
12984  
12985  
12986 Roo.util.TaskRunner = function(interval){
12987     interval = interval || 10;
12988     var tasks = [], removeQueue = [];
12989     var id = 0;
12990     var running = false;
12991
12992     var stopThread = function(){
12993         running = false;
12994         clearInterval(id);
12995         id = 0;
12996     };
12997
12998     var startThread = function(){
12999         if(!running){
13000             running = true;
13001             id = setInterval(runTasks, interval);
13002         }
13003     };
13004
13005     var removeTask = function(task){
13006         removeQueue.push(task);
13007         if(task.onStop){
13008             task.onStop();
13009         }
13010     };
13011
13012     var runTasks = function(){
13013         if(removeQueue.length > 0){
13014             for(var i = 0, len = removeQueue.length; i < len; i++){
13015                 tasks.remove(removeQueue[i]);
13016             }
13017             removeQueue = [];
13018             if(tasks.length < 1){
13019                 stopThread();
13020                 return;
13021             }
13022         }
13023         var now = new Date().getTime();
13024         for(var i = 0, len = tasks.length; i < len; ++i){
13025             var t = tasks[i];
13026             var itime = now - t.taskRunTime;
13027             if(t.interval <= itime){
13028                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13029                 t.taskRunTime = now;
13030                 if(rt === false || t.taskRunCount === t.repeat){
13031                     removeTask(t);
13032                     return;
13033                 }
13034             }
13035             if(t.duration && t.duration <= (now - t.taskStartTime)){
13036                 removeTask(t);
13037             }
13038         }
13039     };
13040
13041     /**
13042      * Queues a new task.
13043      * @param {Object} task
13044      */
13045     this.start = function(task){
13046         tasks.push(task);
13047         task.taskStartTime = new Date().getTime();
13048         task.taskRunTime = 0;
13049         task.taskRunCount = 0;
13050         startThread();
13051         return task;
13052     };
13053
13054     this.stop = function(task){
13055         removeTask(task);
13056         return task;
13057     };
13058
13059     this.stopAll = function(){
13060         stopThread();
13061         for(var i = 0, len = tasks.length; i < len; i++){
13062             if(tasks[i].onStop){
13063                 tasks[i].onStop();
13064             }
13065         }
13066         tasks = [];
13067         removeQueue = [];
13068     };
13069 };
13070
13071 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13072  * Based on:
13073  * Ext JS Library 1.1.1
13074  * Copyright(c) 2006-2007, Ext JS, LLC.
13075  *
13076  * Originally Released Under LGPL - original licence link has changed is not relivant.
13077  *
13078  * Fork - LGPL
13079  * <script type="text/javascript">
13080  */
13081
13082  
13083 /**
13084  * @class Roo.util.MixedCollection
13085  * @extends Roo.util.Observable
13086  * A Collection class that maintains both numeric indexes and keys and exposes events.
13087  * @constructor
13088  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13089  * collection (defaults to false)
13090  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13091  * and return the key value for that item.  This is used when available to look up the key on items that
13092  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13093  * equivalent to providing an implementation for the {@link #getKey} method.
13094  */
13095 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13096     this.items = [];
13097     this.map = {};
13098     this.keys = [];
13099     this.length = 0;
13100     this.addEvents({
13101         /**
13102          * @event clear
13103          * Fires when the collection is cleared.
13104          */
13105         "clear" : true,
13106         /**
13107          * @event add
13108          * Fires when an item is added to the collection.
13109          * @param {Number} index The index at which the item was added.
13110          * @param {Object} o The item added.
13111          * @param {String} key The key associated with the added item.
13112          */
13113         "add" : true,
13114         /**
13115          * @event replace
13116          * Fires when an item is replaced in the collection.
13117          * @param {String} key he key associated with the new added.
13118          * @param {Object} old The item being replaced.
13119          * @param {Object} new The new item.
13120          */
13121         "replace" : true,
13122         /**
13123          * @event remove
13124          * Fires when an item is removed from the collection.
13125          * @param {Object} o The item being removed.
13126          * @param {String} key (optional) The key associated with the removed item.
13127          */
13128         "remove" : true,
13129         "sort" : true
13130     });
13131     this.allowFunctions = allowFunctions === true;
13132     if(keyFn){
13133         this.getKey = keyFn;
13134     }
13135     Roo.util.MixedCollection.superclass.constructor.call(this);
13136 };
13137
13138 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13139     allowFunctions : false,
13140     
13141 /**
13142  * Adds an item to the collection.
13143  * @param {String} key The key to associate with the item
13144  * @param {Object} o The item to add.
13145  * @return {Object} The item added.
13146  */
13147     add : function(key, o){
13148         if(arguments.length == 1){
13149             o = arguments[0];
13150             key = this.getKey(o);
13151         }
13152         if(typeof key == "undefined" || key === null){
13153             this.length++;
13154             this.items.push(o);
13155             this.keys.push(null);
13156         }else{
13157             var old = this.map[key];
13158             if(old){
13159                 return this.replace(key, o);
13160             }
13161             this.length++;
13162             this.items.push(o);
13163             this.map[key] = o;
13164             this.keys.push(key);
13165         }
13166         this.fireEvent("add", this.length-1, o, key);
13167         return o;
13168     },
13169        
13170 /**
13171   * MixedCollection has a generic way to fetch keys if you implement getKey.
13172 <pre><code>
13173 // normal way
13174 var mc = new Roo.util.MixedCollection();
13175 mc.add(someEl.dom.id, someEl);
13176 mc.add(otherEl.dom.id, otherEl);
13177 //and so on
13178
13179 // using getKey
13180 var mc = new Roo.util.MixedCollection();
13181 mc.getKey = function(el){
13182    return el.dom.id;
13183 };
13184 mc.add(someEl);
13185 mc.add(otherEl);
13186
13187 // or via the constructor
13188 var mc = new Roo.util.MixedCollection(false, function(el){
13189    return el.dom.id;
13190 });
13191 mc.add(someEl);
13192 mc.add(otherEl);
13193 </code></pre>
13194  * @param o {Object} The item for which to find the key.
13195  * @return {Object} The key for the passed item.
13196  */
13197     getKey : function(o){
13198          return o.id; 
13199     },
13200    
13201 /**
13202  * Replaces an item in the collection.
13203  * @param {String} key The key associated with the item to replace, or the item to replace.
13204  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13205  * @return {Object}  The new item.
13206  */
13207     replace : function(key, o){
13208         if(arguments.length == 1){
13209             o = arguments[0];
13210             key = this.getKey(o);
13211         }
13212         var old = this.item(key);
13213         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13214              return this.add(key, o);
13215         }
13216         var index = this.indexOfKey(key);
13217         this.items[index] = o;
13218         this.map[key] = o;
13219         this.fireEvent("replace", key, old, o);
13220         return o;
13221     },
13222    
13223 /**
13224  * Adds all elements of an Array or an Object to the collection.
13225  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13226  * an Array of values, each of which are added to the collection.
13227  */
13228     addAll : function(objs){
13229         if(arguments.length > 1 || objs instanceof Array){
13230             var args = arguments.length > 1 ? arguments : objs;
13231             for(var i = 0, len = args.length; i < len; i++){
13232                 this.add(args[i]);
13233             }
13234         }else{
13235             for(var key in objs){
13236                 if(this.allowFunctions || typeof objs[key] != "function"){
13237                     this.add(key, objs[key]);
13238                 }
13239             }
13240         }
13241     },
13242    
13243 /**
13244  * Executes the specified function once for every item in the collection, passing each
13245  * item as the first and only parameter. returning false from the function will stop the iteration.
13246  * @param {Function} fn The function to execute for each item.
13247  * @param {Object} scope (optional) The scope in which to execute the function.
13248  */
13249     each : function(fn, scope){
13250         var items = [].concat(this.items); // each safe for removal
13251         for(var i = 0, len = items.length; i < len; i++){
13252             if(fn.call(scope || items[i], items[i], i, len) === false){
13253                 break;
13254             }
13255         }
13256     },
13257    
13258 /**
13259  * Executes the specified function once for every key in the collection, passing each
13260  * key, and its associated item as the first two parameters.
13261  * @param {Function} fn The function to execute for each item.
13262  * @param {Object} scope (optional) The scope in which to execute the function.
13263  */
13264     eachKey : function(fn, scope){
13265         for(var i = 0, len = this.keys.length; i < len; i++){
13266             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13267         }
13268     },
13269    
13270 /**
13271  * Returns the first item in the collection which elicits a true return value from the
13272  * passed selection function.
13273  * @param {Function} fn The selection function to execute for each item.
13274  * @param {Object} scope (optional) The scope in which to execute the function.
13275  * @return {Object} The first item in the collection which returned true from the selection function.
13276  */
13277     find : function(fn, scope){
13278         for(var i = 0, len = this.items.length; i < len; i++){
13279             if(fn.call(scope || window, this.items[i], this.keys[i])){
13280                 return this.items[i];
13281             }
13282         }
13283         return null;
13284     },
13285    
13286 /**
13287  * Inserts an item at the specified index in the collection.
13288  * @param {Number} index The index to insert the item at.
13289  * @param {String} key The key to associate with the new item, or the item itself.
13290  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13291  * @return {Object} The item inserted.
13292  */
13293     insert : function(index, key, o){
13294         if(arguments.length == 2){
13295             o = arguments[1];
13296             key = this.getKey(o);
13297         }
13298         if(index >= this.length){
13299             return this.add(key, o);
13300         }
13301         this.length++;
13302         this.items.splice(index, 0, o);
13303         if(typeof key != "undefined" && key != null){
13304             this.map[key] = o;
13305         }
13306         this.keys.splice(index, 0, key);
13307         this.fireEvent("add", index, o, key);
13308         return o;
13309     },
13310    
13311 /**
13312  * Removed an item from the collection.
13313  * @param {Object} o The item to remove.
13314  * @return {Object} The item removed.
13315  */
13316     remove : function(o){
13317         return this.removeAt(this.indexOf(o));
13318     },
13319    
13320 /**
13321  * Remove an item from a specified index in the collection.
13322  * @param {Number} index The index within the collection of the item to remove.
13323  */
13324     removeAt : function(index){
13325         if(index < this.length && index >= 0){
13326             this.length--;
13327             var o = this.items[index];
13328             this.items.splice(index, 1);
13329             var key = this.keys[index];
13330             if(typeof key != "undefined"){
13331                 delete this.map[key];
13332             }
13333             this.keys.splice(index, 1);
13334             this.fireEvent("remove", o, key);
13335         }
13336     },
13337    
13338 /**
13339  * Removed an item associated with the passed key fom the collection.
13340  * @param {String} key The key of the item to remove.
13341  */
13342     removeKey : function(key){
13343         return this.removeAt(this.indexOfKey(key));
13344     },
13345    
13346 /**
13347  * Returns the number of items in the collection.
13348  * @return {Number} the number of items in the collection.
13349  */
13350     getCount : function(){
13351         return this.length; 
13352     },
13353    
13354 /**
13355  * Returns index within the collection of the passed Object.
13356  * @param {Object} o The item to find the index of.
13357  * @return {Number} index of the item.
13358  */
13359     indexOf : function(o){
13360         if(!this.items.indexOf){
13361             for(var i = 0, len = this.items.length; i < len; i++){
13362                 if(this.items[i] == o) {
13363                     return i;
13364                 }
13365             }
13366             return -1;
13367         }else{
13368             return this.items.indexOf(o);
13369         }
13370     },
13371    
13372 /**
13373  * Returns index within the collection of the passed key.
13374  * @param {String} key The key to find the index of.
13375  * @return {Number} index of the key.
13376  */
13377     indexOfKey : function(key){
13378         if(!this.keys.indexOf){
13379             for(var i = 0, len = this.keys.length; i < len; i++){
13380                 if(this.keys[i] == key) {
13381                     return i;
13382                 }
13383             }
13384             return -1;
13385         }else{
13386             return this.keys.indexOf(key);
13387         }
13388     },
13389    
13390 /**
13391  * Returns the item associated with the passed key OR index. Key has priority over index.
13392  * @param {String/Number} key The key or index of the item.
13393  * @return {Object} The item associated with the passed key.
13394  */
13395     item : function(key){
13396         if (key === 'length') {
13397             return null;
13398         }
13399         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13400         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13401     },
13402     
13403 /**
13404  * Returns the item at the specified index.
13405  * @param {Number} index The index of the item.
13406  * @return {Object}
13407  */
13408     itemAt : function(index){
13409         return this.items[index];
13410     },
13411     
13412 /**
13413  * Returns the item associated with the passed key.
13414  * @param {String/Number} key The key of the item.
13415  * @return {Object} The item associated with the passed key.
13416  */
13417     key : function(key){
13418         return this.map[key];
13419     },
13420    
13421 /**
13422  * Returns true if the collection contains the passed Object as an item.
13423  * @param {Object} o  The Object to look for in the collection.
13424  * @return {Boolean} True if the collection contains the Object as an item.
13425  */
13426     contains : function(o){
13427         return this.indexOf(o) != -1;
13428     },
13429    
13430 /**
13431  * Returns true if the collection contains the passed Object as a key.
13432  * @param {String} key The key to look for in the collection.
13433  * @return {Boolean} True if the collection contains the Object as a key.
13434  */
13435     containsKey : function(key){
13436         return typeof this.map[key] != "undefined";
13437     },
13438    
13439 /**
13440  * Removes all items from the collection.
13441  */
13442     clear : function(){
13443         this.length = 0;
13444         this.items = [];
13445         this.keys = [];
13446         this.map = {};
13447         this.fireEvent("clear");
13448     },
13449    
13450 /**
13451  * Returns the first item in the collection.
13452  * @return {Object} the first item in the collection..
13453  */
13454     first : function(){
13455         return this.items[0]; 
13456     },
13457    
13458 /**
13459  * Returns the last item in the collection.
13460  * @return {Object} the last item in the collection..
13461  */
13462     last : function(){
13463         return this.items[this.length-1];   
13464     },
13465     
13466     _sort : function(property, dir, fn){
13467         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13468         fn = fn || function(a, b){
13469             return a-b;
13470         };
13471         var c = [], k = this.keys, items = this.items;
13472         for(var i = 0, len = items.length; i < len; i++){
13473             c[c.length] = {key: k[i], value: items[i], index: i};
13474         }
13475         c.sort(function(a, b){
13476             var v = fn(a[property], b[property]) * dsc;
13477             if(v == 0){
13478                 v = (a.index < b.index ? -1 : 1);
13479             }
13480             return v;
13481         });
13482         for(var i = 0, len = c.length; i < len; i++){
13483             items[i] = c[i].value;
13484             k[i] = c[i].key;
13485         }
13486         this.fireEvent("sort", this);
13487     },
13488     
13489     /**
13490      * Sorts this collection with the passed comparison function
13491      * @param {String} direction (optional) "ASC" or "DESC"
13492      * @param {Function} fn (optional) comparison function
13493      */
13494     sort : function(dir, fn){
13495         this._sort("value", dir, fn);
13496     },
13497     
13498     /**
13499      * Sorts this collection by keys
13500      * @param {String} direction (optional) "ASC" or "DESC"
13501      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13502      */
13503     keySort : function(dir, fn){
13504         this._sort("key", dir, fn || function(a, b){
13505             return String(a).toUpperCase()-String(b).toUpperCase();
13506         });
13507     },
13508     
13509     /**
13510      * Returns a range of items in this collection
13511      * @param {Number} startIndex (optional) defaults to 0
13512      * @param {Number} endIndex (optional) default to the last item
13513      * @return {Array} An array of items
13514      */
13515     getRange : function(start, end){
13516         var items = this.items;
13517         if(items.length < 1){
13518             return [];
13519         }
13520         start = start || 0;
13521         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13522         var r = [];
13523         if(start <= end){
13524             for(var i = start; i <= end; i++) {
13525                     r[r.length] = items[i];
13526             }
13527         }else{
13528             for(var i = start; i >= end; i--) {
13529                     r[r.length] = items[i];
13530             }
13531         }
13532         return r;
13533     },
13534         
13535     /**
13536      * Filter the <i>objects</i> in this collection by a specific property. 
13537      * Returns a new collection that has been filtered.
13538      * @param {String} property A property on your objects
13539      * @param {String/RegExp} value Either string that the property values 
13540      * should start with or a RegExp to test against the property
13541      * @return {MixedCollection} The new filtered collection
13542      */
13543     filter : function(property, value){
13544         if(!value.exec){ // not a regex
13545             value = String(value);
13546             if(value.length == 0){
13547                 return this.clone();
13548             }
13549             value = new RegExp("^" + Roo.escapeRe(value), "i");
13550         }
13551         return this.filterBy(function(o){
13552             return o && value.test(o[property]);
13553         });
13554         },
13555     
13556     /**
13557      * Filter by a function. * Returns a new collection that has been filtered.
13558      * The passed function will be called with each 
13559      * object in the collection. If the function returns true, the value is included 
13560      * otherwise it is filtered.
13561      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13562      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13563      * @return {MixedCollection} The new filtered collection
13564      */
13565     filterBy : function(fn, scope){
13566         var r = new Roo.util.MixedCollection();
13567         r.getKey = this.getKey;
13568         var k = this.keys, it = this.items;
13569         for(var i = 0, len = it.length; i < len; i++){
13570             if(fn.call(scope||this, it[i], k[i])){
13571                                 r.add(k[i], it[i]);
13572                         }
13573         }
13574         return r;
13575     },
13576     
13577     /**
13578      * Creates a duplicate of this collection
13579      * @return {MixedCollection}
13580      */
13581     clone : function(){
13582         var r = new Roo.util.MixedCollection();
13583         var k = this.keys, it = this.items;
13584         for(var i = 0, len = it.length; i < len; i++){
13585             r.add(k[i], it[i]);
13586         }
13587         r.getKey = this.getKey;
13588         return r;
13589     }
13590 });
13591 /**
13592  * Returns the item associated with the passed key or index.
13593  * @method
13594  * @param {String/Number} key The key or index of the item.
13595  * @return {Object} The item associated with the passed key.
13596  */
13597 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13598  * Based on:
13599  * Ext JS Library 1.1.1
13600  * Copyright(c) 2006-2007, Ext JS, LLC.
13601  *
13602  * Originally Released Under LGPL - original licence link has changed is not relivant.
13603  *
13604  * Fork - LGPL
13605  * <script type="text/javascript">
13606  */
13607 /**
13608  * @class Roo.util.JSON
13609  * Modified version of Douglas Crockford"s json.js that doesn"t
13610  * mess with the Object prototype 
13611  * http://www.json.org/js.html
13612  * @singleton
13613  */
13614 Roo.util.JSON = new (function(){
13615     var useHasOwn = {}.hasOwnProperty ? true : false;
13616     
13617     // crashes Safari in some instances
13618     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13619     
13620     var pad = function(n) {
13621         return n < 10 ? "0" + n : n;
13622     };
13623     
13624     var m = {
13625         "\b": '\\b',
13626         "\t": '\\t',
13627         "\n": '\\n',
13628         "\f": '\\f',
13629         "\r": '\\r',
13630         '"' : '\\"',
13631         "\\": '\\\\'
13632     };
13633
13634     var encodeString = function(s){
13635         if (/["\\\x00-\x1f]/.test(s)) {
13636             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13637                 var c = m[b];
13638                 if(c){
13639                     return c;
13640                 }
13641                 c = b.charCodeAt();
13642                 return "\\u00" +
13643                     Math.floor(c / 16).toString(16) +
13644                     (c % 16).toString(16);
13645             }) + '"';
13646         }
13647         return '"' + s + '"';
13648     };
13649     
13650     var encodeArray = function(o){
13651         var a = ["["], b, i, l = o.length, v;
13652             for (i = 0; i < l; i += 1) {
13653                 v = o[i];
13654                 switch (typeof v) {
13655                     case "undefined":
13656                     case "function":
13657                     case "unknown":
13658                         break;
13659                     default:
13660                         if (b) {
13661                             a.push(',');
13662                         }
13663                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13664                         b = true;
13665                 }
13666             }
13667             a.push("]");
13668             return a.join("");
13669     };
13670     
13671     var encodeDate = function(o){
13672         return '"' + o.getFullYear() + "-" +
13673                 pad(o.getMonth() + 1) + "-" +
13674                 pad(o.getDate()) + "T" +
13675                 pad(o.getHours()) + ":" +
13676                 pad(o.getMinutes()) + ":" +
13677                 pad(o.getSeconds()) + '"';
13678     };
13679     
13680     /**
13681      * Encodes an Object, Array or other value
13682      * @param {Mixed} o The variable to encode
13683      * @return {String} The JSON string
13684      */
13685     this.encode = function(o)
13686     {
13687         // should this be extended to fully wrap stringify..
13688         
13689         if(typeof o == "undefined" || o === null){
13690             return "null";
13691         }else if(o instanceof Array){
13692             return encodeArray(o);
13693         }else if(o instanceof Date){
13694             return encodeDate(o);
13695         }else if(typeof o == "string"){
13696             return encodeString(o);
13697         }else if(typeof o == "number"){
13698             return isFinite(o) ? String(o) : "null";
13699         }else if(typeof o == "boolean"){
13700             return String(o);
13701         }else {
13702             var a = ["{"], b, i, v;
13703             for (i in o) {
13704                 if(!useHasOwn || o.hasOwnProperty(i)) {
13705                     v = o[i];
13706                     switch (typeof v) {
13707                     case "undefined":
13708                     case "function":
13709                     case "unknown":
13710                         break;
13711                     default:
13712                         if(b){
13713                             a.push(',');
13714                         }
13715                         a.push(this.encode(i), ":",
13716                                 v === null ? "null" : this.encode(v));
13717                         b = true;
13718                     }
13719                 }
13720             }
13721             a.push("}");
13722             return a.join("");
13723         }
13724     };
13725     
13726     /**
13727      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13728      * @param {String} json The JSON string
13729      * @return {Object} The resulting object
13730      */
13731     this.decode = function(json){
13732         
13733         return  /** eval:var:json */ eval("(" + json + ')');
13734     };
13735 })();
13736 /** 
13737  * Shorthand for {@link Roo.util.JSON#encode}
13738  * @member Roo encode 
13739  * @method */
13740 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13741 /** 
13742  * Shorthand for {@link Roo.util.JSON#decode}
13743  * @member Roo decode 
13744  * @method */
13745 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13746 /*
13747  * Based on:
13748  * Ext JS Library 1.1.1
13749  * Copyright(c) 2006-2007, Ext JS, LLC.
13750  *
13751  * Originally Released Under LGPL - original licence link has changed is not relivant.
13752  *
13753  * Fork - LGPL
13754  * <script type="text/javascript">
13755  */
13756  
13757 /**
13758  * @class Roo.util.Format
13759  * Reusable data formatting functions
13760  * @singleton
13761  */
13762 Roo.util.Format = function(){
13763     var trimRe = /^\s+|\s+$/g;
13764     return {
13765         /**
13766          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13767          * @param {String} value The string to truncate
13768          * @param {Number} length The maximum length to allow before truncating
13769          * @return {String} The converted text
13770          */
13771         ellipsis : function(value, len){
13772             if(value && value.length > len){
13773                 return value.substr(0, len-3)+"...";
13774             }
13775             return value;
13776         },
13777
13778         /**
13779          * Checks a reference and converts it to empty string if it is undefined
13780          * @param {Mixed} value Reference to check
13781          * @return {Mixed} Empty string if converted, otherwise the original value
13782          */
13783         undef : function(value){
13784             return typeof value != "undefined" ? value : "";
13785         },
13786
13787         /**
13788          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13789          * @param {String} value The string to encode
13790          * @return {String} The encoded text
13791          */
13792         htmlEncode : function(value){
13793             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13794         },
13795
13796         /**
13797          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13798          * @param {String} value The string to decode
13799          * @return {String} The decoded text
13800          */
13801         htmlDecode : function(value){
13802             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13803         },
13804
13805         /**
13806          * Trims any whitespace from either side of a string
13807          * @param {String} value The text to trim
13808          * @return {String} The trimmed text
13809          */
13810         trim : function(value){
13811             return String(value).replace(trimRe, "");
13812         },
13813
13814         /**
13815          * Returns a substring from within an original string
13816          * @param {String} value The original text
13817          * @param {Number} start The start index of the substring
13818          * @param {Number} length The length of the substring
13819          * @return {String} The substring
13820          */
13821         substr : function(value, start, length){
13822             return String(value).substr(start, length);
13823         },
13824
13825         /**
13826          * Converts a string to all lower case letters
13827          * @param {String} value The text to convert
13828          * @return {String} The converted text
13829          */
13830         lowercase : function(value){
13831             return String(value).toLowerCase();
13832         },
13833
13834         /**
13835          * Converts a string to all upper case letters
13836          * @param {String} value The text to convert
13837          * @return {String} The converted text
13838          */
13839         uppercase : function(value){
13840             return String(value).toUpperCase();
13841         },
13842
13843         /**
13844          * Converts the first character only of a string to upper case
13845          * @param {String} value The text to convert
13846          * @return {String} The converted text
13847          */
13848         capitalize : function(value){
13849             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13850         },
13851
13852         // private
13853         call : function(value, fn){
13854             if(arguments.length > 2){
13855                 var args = Array.prototype.slice.call(arguments, 2);
13856                 args.unshift(value);
13857                  
13858                 return /** eval:var:value */  eval(fn).apply(window, args);
13859             }else{
13860                 /** eval:var:value */
13861                 return /** eval:var:value */ eval(fn).call(window, value);
13862             }
13863         },
13864
13865        
13866         /**
13867          * safer version of Math.toFixed..??/
13868          * @param {Number/String} value The numeric value to format
13869          * @param {Number/String} value Decimal places 
13870          * @return {String} The formatted currency string
13871          */
13872         toFixed : function(v, n)
13873         {
13874             // why not use to fixed - precision is buggered???
13875             if (!n) {
13876                 return Math.round(v-0);
13877             }
13878             var fact = Math.pow(10,n+1);
13879             v = (Math.round((v-0)*fact))/fact;
13880             var z = (''+fact).substring(2);
13881             if (v == Math.floor(v)) {
13882                 return Math.floor(v) + '.' + z;
13883             }
13884             
13885             // now just padd decimals..
13886             var ps = String(v).split('.');
13887             var fd = (ps[1] + z);
13888             var r = fd.substring(0,n); 
13889             var rm = fd.substring(n); 
13890             if (rm < 5) {
13891                 return ps[0] + '.' + r;
13892             }
13893             r*=1; // turn it into a number;
13894             r++;
13895             if (String(r).length != n) {
13896                 ps[0]*=1;
13897                 ps[0]++;
13898                 r = String(r).substring(1); // chop the end off.
13899             }
13900             
13901             return ps[0] + '.' + r;
13902              
13903         },
13904         
13905         /**
13906          * Format a number as US currency
13907          * @param {Number/String} value The numeric value to format
13908          * @return {String} The formatted currency string
13909          */
13910         usMoney : function(v){
13911             return '$' + Roo.util.Format.number(v);
13912         },
13913         
13914         /**
13915          * Format a number
13916          * eventually this should probably emulate php's number_format
13917          * @param {Number/String} value The numeric value to format
13918          * @param {Number} decimals number of decimal places
13919          * @param {String} delimiter for thousands (default comma)
13920          * @return {String} The formatted currency string
13921          */
13922         number : function(v, decimals, thousandsDelimiter)
13923         {
13924             // multiply and round.
13925             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13926             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13927             
13928             var mul = Math.pow(10, decimals);
13929             var zero = String(mul).substring(1);
13930             v = (Math.round((v-0)*mul))/mul;
13931             
13932             // if it's '0' number.. then
13933             
13934             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13935             v = String(v);
13936             var ps = v.split('.');
13937             var whole = ps[0];
13938             
13939             var r = /(\d+)(\d{3})/;
13940             // add comma's
13941             
13942             if(thousandsDelimiter.length != 0) {
13943                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13944             } 
13945             
13946             var sub = ps[1] ?
13947                     // has decimals..
13948                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13949                     // does not have decimals
13950                     (decimals ? ('.' + zero) : '');
13951             
13952             
13953             return whole + sub ;
13954         },
13955         
13956         /**
13957          * Parse a value into a formatted date using the specified format pattern.
13958          * @param {Mixed} value The value to format
13959          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13960          * @return {String} The formatted date string
13961          */
13962         date : function(v, format){
13963             if(!v){
13964                 return "";
13965             }
13966             if(!(v instanceof Date)){
13967                 v = new Date(Date.parse(v));
13968             }
13969             return v.dateFormat(format || Roo.util.Format.defaults.date);
13970         },
13971
13972         /**
13973          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13974          * @param {String} format Any valid date format string
13975          * @return {Function} The date formatting function
13976          */
13977         dateRenderer : function(format){
13978             return function(v){
13979                 return Roo.util.Format.date(v, format);  
13980             };
13981         },
13982
13983         // private
13984         stripTagsRE : /<\/?[^>]+>/gi,
13985         
13986         /**
13987          * Strips all HTML tags
13988          * @param {Mixed} value The text from which to strip tags
13989          * @return {String} The stripped text
13990          */
13991         stripTags : function(v){
13992             return !v ? v : String(v).replace(this.stripTagsRE, "");
13993         },
13994         /**
13995          * Size in Mb,Gb etc.
13996          * @param {Number} value The number to be formated
13997          * @param {number} decimals how many decimal places
13998          * @return {String} the formated string
13999          */
14000         size : function(value, decimals)
14001         {
14002             var sizes = ['b', 'k', 'M', 'G', 'T'];
14003             if (v == 0) {
14004                 return 0;
14005             }
14006             var i = parseInt(Math.floor(Math.log(v) / Math.log(1024)));
14007             return this.number(v / Math.pow(1024, i) ,decimals) + ' ' + sizes[i];
14008         }
14009         
14010         
14011         
14012     };
14013 }();
14014 Roo.util.Format.defaults = {
14015     date : 'd/M/Y'
14016 };/*
14017  * Based on:
14018  * Ext JS Library 1.1.1
14019  * Copyright(c) 2006-2007, Ext JS, LLC.
14020  *
14021  * Originally Released Under LGPL - original licence link has changed is not relivant.
14022  *
14023  * Fork - LGPL
14024  * <script type="text/javascript">
14025  */
14026
14027
14028  
14029
14030 /**
14031  * @class Roo.MasterTemplate
14032  * @extends Roo.Template
14033  * Provides a template that can have child templates. The syntax is:
14034 <pre><code>
14035 var t = new Roo.MasterTemplate(
14036         '&lt;select name="{name}"&gt;',
14037                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14038         '&lt;/select&gt;'
14039 );
14040 t.add('options', {value: 'foo', text: 'bar'});
14041 // or you can add multiple child elements in one shot
14042 t.addAll('options', [
14043     {value: 'foo', text: 'bar'},
14044     {value: 'foo2', text: 'bar2'},
14045     {value: 'foo3', text: 'bar3'}
14046 ]);
14047 // then append, applying the master template values
14048 t.append('my-form', {name: 'my-select'});
14049 </code></pre>
14050 * A name attribute for the child template is not required if you have only one child
14051 * template or you want to refer to them by index.
14052  */
14053 Roo.MasterTemplate = function(){
14054     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14055     this.originalHtml = this.html;
14056     var st = {};
14057     var m, re = this.subTemplateRe;
14058     re.lastIndex = 0;
14059     var subIndex = 0;
14060     while(m = re.exec(this.html)){
14061         var name = m[1], content = m[2];
14062         st[subIndex] = {
14063             name: name,
14064             index: subIndex,
14065             buffer: [],
14066             tpl : new Roo.Template(content)
14067         };
14068         if(name){
14069             st[name] = st[subIndex];
14070         }
14071         st[subIndex].tpl.compile();
14072         st[subIndex].tpl.call = this.call.createDelegate(this);
14073         subIndex++;
14074     }
14075     this.subCount = subIndex;
14076     this.subs = st;
14077 };
14078 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14079     /**
14080     * The regular expression used to match sub templates
14081     * @type RegExp
14082     * @property
14083     */
14084     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14085
14086     /**
14087      * Applies the passed values to a child template.
14088      * @param {String/Number} name (optional) The name or index of the child template
14089      * @param {Array/Object} values The values to be applied to the template
14090      * @return {MasterTemplate} this
14091      */
14092      add : function(name, values){
14093         if(arguments.length == 1){
14094             values = arguments[0];
14095             name = 0;
14096         }
14097         var s = this.subs[name];
14098         s.buffer[s.buffer.length] = s.tpl.apply(values);
14099         return this;
14100     },
14101
14102     /**
14103      * Applies all the passed values to a child template.
14104      * @param {String/Number} name (optional) The name or index of the child template
14105      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14106      * @param {Boolean} reset (optional) True to reset the template first
14107      * @return {MasterTemplate} this
14108      */
14109     fill : function(name, values, reset){
14110         var a = arguments;
14111         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14112             values = a[0];
14113             name = 0;
14114             reset = a[1];
14115         }
14116         if(reset){
14117             this.reset();
14118         }
14119         for(var i = 0, len = values.length; i < len; i++){
14120             this.add(name, values[i]);
14121         }
14122         return this;
14123     },
14124
14125     /**
14126      * Resets the template for reuse
14127      * @return {MasterTemplate} this
14128      */
14129      reset : function(){
14130         var s = this.subs;
14131         for(var i = 0; i < this.subCount; i++){
14132             s[i].buffer = [];
14133         }
14134         return this;
14135     },
14136
14137     applyTemplate : function(values){
14138         var s = this.subs;
14139         var replaceIndex = -1;
14140         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14141             return s[++replaceIndex].buffer.join("");
14142         });
14143         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14144     },
14145
14146     apply : function(){
14147         return this.applyTemplate.apply(this, arguments);
14148     },
14149
14150     compile : function(){return this;}
14151 });
14152
14153 /**
14154  * Alias for fill().
14155  * @method
14156  */
14157 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14158  /**
14159  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14160  * var tpl = Roo.MasterTemplate.from('element-id');
14161  * @param {String/HTMLElement} el
14162  * @param {Object} config
14163  * @static
14164  */
14165 Roo.MasterTemplate.from = function(el, config){
14166     el = Roo.getDom(el);
14167     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14168 };/*
14169  * Based on:
14170  * Ext JS Library 1.1.1
14171  * Copyright(c) 2006-2007, Ext JS, LLC.
14172  *
14173  * Originally Released Under LGPL - original licence link has changed is not relivant.
14174  *
14175  * Fork - LGPL
14176  * <script type="text/javascript">
14177  */
14178
14179  
14180 /**
14181  * @class Roo.util.CSS
14182  * Utility class for manipulating CSS rules
14183  * @singleton
14184  */
14185 Roo.util.CSS = function(){
14186         var rules = null;
14187         var doc = document;
14188
14189     var camelRe = /(-[a-z])/gi;
14190     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14191
14192    return {
14193    /**
14194     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14195     * tag and appended to the HEAD of the document.
14196     * @param {String|Object} cssText The text containing the css rules
14197     * @param {String} id An id to add to the stylesheet for later removal
14198     * @return {StyleSheet}
14199     */
14200     createStyleSheet : function(cssText, id){
14201         var ss;
14202         var head = doc.getElementsByTagName("head")[0];
14203         var nrules = doc.createElement("style");
14204         nrules.setAttribute("type", "text/css");
14205         if(id){
14206             nrules.setAttribute("id", id);
14207         }
14208         if (typeof(cssText) != 'string') {
14209             // support object maps..
14210             // not sure if this a good idea.. 
14211             // perhaps it should be merged with the general css handling
14212             // and handle js style props.
14213             var cssTextNew = [];
14214             for(var n in cssText) {
14215                 var citems = [];
14216                 for(var k in cssText[n]) {
14217                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14218                 }
14219                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14220                 
14221             }
14222             cssText = cssTextNew.join("\n");
14223             
14224         }
14225        
14226        
14227        if(Roo.isIE){
14228            head.appendChild(nrules);
14229            ss = nrules.styleSheet;
14230            ss.cssText = cssText;
14231        }else{
14232            try{
14233                 nrules.appendChild(doc.createTextNode(cssText));
14234            }catch(e){
14235                nrules.cssText = cssText; 
14236            }
14237            head.appendChild(nrules);
14238            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14239        }
14240        this.cacheStyleSheet(ss);
14241        return ss;
14242    },
14243
14244    /**
14245     * Removes a style or link tag by id
14246     * @param {String} id The id of the tag
14247     */
14248    removeStyleSheet : function(id){
14249        var existing = doc.getElementById(id);
14250        if(existing){
14251            existing.parentNode.removeChild(existing);
14252        }
14253    },
14254
14255    /**
14256     * Dynamically swaps an existing stylesheet reference for a new one
14257     * @param {String} id The id of an existing link tag to remove
14258     * @param {String} url The href of the new stylesheet to include
14259     */
14260    swapStyleSheet : function(id, url){
14261        this.removeStyleSheet(id);
14262        var ss = doc.createElement("link");
14263        ss.setAttribute("rel", "stylesheet");
14264        ss.setAttribute("type", "text/css");
14265        ss.setAttribute("id", id);
14266        ss.setAttribute("href", url);
14267        doc.getElementsByTagName("head")[0].appendChild(ss);
14268    },
14269    
14270    /**
14271     * Refresh the rule cache if you have dynamically added stylesheets
14272     * @return {Object} An object (hash) of rules indexed by selector
14273     */
14274    refreshCache : function(){
14275        return this.getRules(true);
14276    },
14277
14278    // private
14279    cacheStyleSheet : function(stylesheet){
14280        if(!rules){
14281            rules = {};
14282        }
14283        try{// try catch for cross domain access issue
14284            var ssRules = stylesheet.cssRules || stylesheet.rules;
14285            for(var j = ssRules.length-1; j >= 0; --j){
14286                rules[ssRules[j].selectorText] = ssRules[j];
14287            }
14288        }catch(e){}
14289    },
14290    
14291    /**
14292     * Gets all css rules for the document
14293     * @param {Boolean} refreshCache true to refresh the internal cache
14294     * @return {Object} An object (hash) of rules indexed by selector
14295     */
14296    getRules : function(refreshCache){
14297                 if(rules == null || refreshCache){
14298                         rules = {};
14299                         var ds = doc.styleSheets;
14300                         for(var i =0, len = ds.length; i < len; i++){
14301                             try{
14302                         this.cacheStyleSheet(ds[i]);
14303                     }catch(e){} 
14304                 }
14305                 }
14306                 return rules;
14307         },
14308         
14309         /**
14310     * Gets an an individual CSS rule by selector(s)
14311     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14312     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14313     * @return {CSSRule} The CSS rule or null if one is not found
14314     */
14315    getRule : function(selector, refreshCache){
14316                 var rs = this.getRules(refreshCache);
14317                 if(!(selector instanceof Array)){
14318                     return rs[selector];
14319                 }
14320                 for(var i = 0; i < selector.length; i++){
14321                         if(rs[selector[i]]){
14322                                 return rs[selector[i]];
14323                         }
14324                 }
14325                 return null;
14326         },
14327         
14328         
14329         /**
14330     * Updates a rule property
14331     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14332     * @param {String} property The css property
14333     * @param {String} value The new value for the property
14334     * @return {Boolean} true If a rule was found and updated
14335     */
14336    updateRule : function(selector, property, value){
14337                 if(!(selector instanceof Array)){
14338                         var rule = this.getRule(selector);
14339                         if(rule){
14340                                 rule.style[property.replace(camelRe, camelFn)] = value;
14341                                 return true;
14342                         }
14343                 }else{
14344                         for(var i = 0; i < selector.length; i++){
14345                                 if(this.updateRule(selector[i], property, value)){
14346                                         return true;
14347                                 }
14348                         }
14349                 }
14350                 return false;
14351         }
14352    };   
14353 }();/*
14354  * Based on:
14355  * Ext JS Library 1.1.1
14356  * Copyright(c) 2006-2007, Ext JS, LLC.
14357  *
14358  * Originally Released Under LGPL - original licence link has changed is not relivant.
14359  *
14360  * Fork - LGPL
14361  * <script type="text/javascript">
14362  */
14363
14364  
14365
14366 /**
14367  * @class Roo.util.ClickRepeater
14368  * @extends Roo.util.Observable
14369  * 
14370  * A wrapper class which can be applied to any element. Fires a "click" event while the
14371  * mouse is pressed. The interval between firings may be specified in the config but
14372  * defaults to 10 milliseconds.
14373  * 
14374  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14375  * 
14376  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14377  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14378  * Similar to an autorepeat key delay.
14379  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14380  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14381  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14382  *           "interval" and "delay" are ignored. "immediate" is honored.
14383  * @cfg {Boolean} preventDefault True to prevent the default click event
14384  * @cfg {Boolean} stopDefault True to stop the default click event
14385  * 
14386  * @history
14387  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14388  *     2007-02-02 jvs Renamed to ClickRepeater
14389  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14390  *
14391  *  @constructor
14392  * @param {String/HTMLElement/Element} el The element to listen on
14393  * @param {Object} config
14394  **/
14395 Roo.util.ClickRepeater = function(el, config)
14396 {
14397     this.el = Roo.get(el);
14398     this.el.unselectable();
14399
14400     Roo.apply(this, config);
14401
14402     this.addEvents({
14403     /**
14404      * @event mousedown
14405      * Fires when the mouse button is depressed.
14406      * @param {Roo.util.ClickRepeater} this
14407      */
14408         "mousedown" : true,
14409     /**
14410      * @event click
14411      * Fires on a specified interval during the time the element is pressed.
14412      * @param {Roo.util.ClickRepeater} this
14413      */
14414         "click" : true,
14415     /**
14416      * @event mouseup
14417      * Fires when the mouse key is released.
14418      * @param {Roo.util.ClickRepeater} this
14419      */
14420         "mouseup" : true
14421     });
14422
14423     this.el.on("mousedown", this.handleMouseDown, this);
14424     if(this.preventDefault || this.stopDefault){
14425         this.el.on("click", function(e){
14426             if(this.preventDefault){
14427                 e.preventDefault();
14428             }
14429             if(this.stopDefault){
14430                 e.stopEvent();
14431             }
14432         }, this);
14433     }
14434
14435     // allow inline handler
14436     if(this.handler){
14437         this.on("click", this.handler,  this.scope || this);
14438     }
14439
14440     Roo.util.ClickRepeater.superclass.constructor.call(this);
14441 };
14442
14443 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14444     interval : 20,
14445     delay: 250,
14446     preventDefault : true,
14447     stopDefault : false,
14448     timer : 0,
14449
14450     // private
14451     handleMouseDown : function(){
14452         clearTimeout(this.timer);
14453         this.el.blur();
14454         if(this.pressClass){
14455             this.el.addClass(this.pressClass);
14456         }
14457         this.mousedownTime = new Date();
14458
14459         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14460         this.el.on("mouseout", this.handleMouseOut, this);
14461
14462         this.fireEvent("mousedown", this);
14463         this.fireEvent("click", this);
14464         
14465         this.timer = this.click.defer(this.delay || this.interval, this);
14466     },
14467
14468     // private
14469     click : function(){
14470         this.fireEvent("click", this);
14471         this.timer = this.click.defer(this.getInterval(), this);
14472     },
14473
14474     // private
14475     getInterval: function(){
14476         if(!this.accelerate){
14477             return this.interval;
14478         }
14479         var pressTime = this.mousedownTime.getElapsed();
14480         if(pressTime < 500){
14481             return 400;
14482         }else if(pressTime < 1700){
14483             return 320;
14484         }else if(pressTime < 2600){
14485             return 250;
14486         }else if(pressTime < 3500){
14487             return 180;
14488         }else if(pressTime < 4400){
14489             return 140;
14490         }else if(pressTime < 5300){
14491             return 80;
14492         }else if(pressTime < 6200){
14493             return 50;
14494         }else{
14495             return 10;
14496         }
14497     },
14498
14499     // private
14500     handleMouseOut : function(){
14501         clearTimeout(this.timer);
14502         if(this.pressClass){
14503             this.el.removeClass(this.pressClass);
14504         }
14505         this.el.on("mouseover", this.handleMouseReturn, this);
14506     },
14507
14508     // private
14509     handleMouseReturn : function(){
14510         this.el.un("mouseover", this.handleMouseReturn);
14511         if(this.pressClass){
14512             this.el.addClass(this.pressClass);
14513         }
14514         this.click();
14515     },
14516
14517     // private
14518     handleMouseUp : function(){
14519         clearTimeout(this.timer);
14520         this.el.un("mouseover", this.handleMouseReturn);
14521         this.el.un("mouseout", this.handleMouseOut);
14522         Roo.get(document).un("mouseup", this.handleMouseUp);
14523         this.el.removeClass(this.pressClass);
14524         this.fireEvent("mouseup", this);
14525     }
14526 });/**
14527  * @class Roo.util.Clipboard
14528  * @static
14529  * 
14530  * Clipboard UTILS
14531  * 
14532  **/
14533 Roo.util.Clipboard = {
14534     /**
14535      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
14536      * @param {String} text to copy to clipboard
14537      */
14538     write : function(text) {
14539         // navigator clipboard api needs a secure context (https)
14540         if (navigator.clipboard && window.isSecureContext) {
14541             // navigator clipboard api method'
14542             navigator.clipboard.writeText(text);
14543             return ;
14544         } 
14545         // text area method
14546         var ta = document.createElement("textarea");
14547         ta.value = text;
14548         // make the textarea out of viewport
14549         ta.style.position = "fixed";
14550         ta.style.left = "-999999px";
14551         ta.style.top = "-999999px";
14552         document.body.appendChild(ta);
14553         ta.focus();
14554         ta.select();
14555         document.execCommand('copy');
14556         (function() {
14557             ta.remove();
14558         }).defer(100);
14559         
14560     }
14561         
14562 }
14563     /*
14564  * Based on:
14565  * Ext JS Library 1.1.1
14566  * Copyright(c) 2006-2007, Ext JS, LLC.
14567  *
14568  * Originally Released Under LGPL - original licence link has changed is not relivant.
14569  *
14570  * Fork - LGPL
14571  * <script type="text/javascript">
14572  */
14573
14574  
14575 /**
14576  * @class Roo.KeyNav
14577  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14578  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14579  * way to implement custom navigation schemes for any UI component.</p>
14580  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14581  * pageUp, pageDown, del, home, end.  Usage:</p>
14582  <pre><code>
14583 var nav = new Roo.KeyNav("my-element", {
14584     "left" : function(e){
14585         this.moveLeft(e.ctrlKey);
14586     },
14587     "right" : function(e){
14588         this.moveRight(e.ctrlKey);
14589     },
14590     "enter" : function(e){
14591         this.save();
14592     },
14593     scope : this
14594 });
14595 </code></pre>
14596  * @constructor
14597  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14598  * @param {Object} config The config
14599  */
14600 Roo.KeyNav = function(el, config){
14601     this.el = Roo.get(el);
14602     Roo.apply(this, config);
14603     if(!this.disabled){
14604         this.disabled = true;
14605         this.enable();
14606     }
14607 };
14608
14609 Roo.KeyNav.prototype = {
14610     /**
14611      * @cfg {Boolean} disabled
14612      * True to disable this KeyNav instance (defaults to false)
14613      */
14614     disabled : false,
14615     /**
14616      * @cfg {String} defaultEventAction
14617      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14618      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14619      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14620      */
14621     defaultEventAction: "stopEvent",
14622     /**
14623      * @cfg {Boolean} forceKeyDown
14624      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14625      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14626      * handle keydown instead of keypress.
14627      */
14628     forceKeyDown : false,
14629
14630     // private
14631     prepareEvent : function(e){
14632         var k = e.getKey();
14633         var h = this.keyToHandler[k];
14634         //if(h && this[h]){
14635         //    e.stopPropagation();
14636         //}
14637         if(Roo.isSafari && h && k >= 37 && k <= 40){
14638             e.stopEvent();
14639         }
14640     },
14641
14642     // private
14643     relay : function(e){
14644         var k = e.getKey();
14645         var h = this.keyToHandler[k];
14646         if(h && this[h]){
14647             if(this.doRelay(e, this[h], h) !== true){
14648                 e[this.defaultEventAction]();
14649             }
14650         }
14651     },
14652
14653     // private
14654     doRelay : function(e, h, hname){
14655         return h.call(this.scope || this, e);
14656     },
14657
14658     // possible handlers
14659     enter : false,
14660     left : false,
14661     right : false,
14662     up : false,
14663     down : false,
14664     tab : false,
14665     esc : false,
14666     pageUp : false,
14667     pageDown : false,
14668     del : false,
14669     home : false,
14670     end : false,
14671
14672     // quick lookup hash
14673     keyToHandler : {
14674         37 : "left",
14675         39 : "right",
14676         38 : "up",
14677         40 : "down",
14678         33 : "pageUp",
14679         34 : "pageDown",
14680         46 : "del",
14681         36 : "home",
14682         35 : "end",
14683         13 : "enter",
14684         27 : "esc",
14685         9  : "tab"
14686     },
14687
14688         /**
14689          * Enable this KeyNav
14690          */
14691         enable: function(){
14692                 if(this.disabled){
14693             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14694             // the EventObject will normalize Safari automatically
14695             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14696                 this.el.on("keydown", this.relay,  this);
14697             }else{
14698                 this.el.on("keydown", this.prepareEvent,  this);
14699                 this.el.on("keypress", this.relay,  this);
14700             }
14701                     this.disabled = false;
14702                 }
14703         },
14704
14705         /**
14706          * Disable this KeyNav
14707          */
14708         disable: function(){
14709                 if(!this.disabled){
14710                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14711                 this.el.un("keydown", this.relay);
14712             }else{
14713                 this.el.un("keydown", this.prepareEvent);
14714                 this.el.un("keypress", this.relay);
14715             }
14716                     this.disabled = true;
14717                 }
14718         }
14719 };/*
14720  * Based on:
14721  * Ext JS Library 1.1.1
14722  * Copyright(c) 2006-2007, Ext JS, LLC.
14723  *
14724  * Originally Released Under LGPL - original licence link has changed is not relivant.
14725  *
14726  * Fork - LGPL
14727  * <script type="text/javascript">
14728  */
14729
14730  
14731 /**
14732  * @class Roo.KeyMap
14733  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14734  * The constructor accepts the same config object as defined by {@link #addBinding}.
14735  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14736  * combination it will call the function with this signature (if the match is a multi-key
14737  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14738  * A KeyMap can also handle a string representation of keys.<br />
14739  * Usage:
14740  <pre><code>
14741 // map one key by key code
14742 var map = new Roo.KeyMap("my-element", {
14743     key: 13, // or Roo.EventObject.ENTER
14744     fn: myHandler,
14745     scope: myObject
14746 });
14747
14748 // map multiple keys to one action by string
14749 var map = new Roo.KeyMap("my-element", {
14750     key: "a\r\n\t",
14751     fn: myHandler,
14752     scope: myObject
14753 });
14754
14755 // map multiple keys to multiple actions by strings and array of codes
14756 var map = new Roo.KeyMap("my-element", [
14757     {
14758         key: [10,13],
14759         fn: function(){ alert("Return was pressed"); }
14760     }, {
14761         key: "abc",
14762         fn: function(){ alert('a, b or c was pressed'); }
14763     }, {
14764         key: "\t",
14765         ctrl:true,
14766         shift:true,
14767         fn: function(){ alert('Control + shift + tab was pressed.'); }
14768     }
14769 ]);
14770 </code></pre>
14771  * <b>Note: A KeyMap starts enabled</b>
14772  * @constructor
14773  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14774  * @param {Object} config The config (see {@link #addBinding})
14775  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14776  */
14777 Roo.KeyMap = function(el, config, eventName){
14778     this.el  = Roo.get(el);
14779     this.eventName = eventName || "keydown";
14780     this.bindings = [];
14781     if(config){
14782         this.addBinding(config);
14783     }
14784     this.enable();
14785 };
14786
14787 Roo.KeyMap.prototype = {
14788     /**
14789      * True to stop the event from bubbling and prevent the default browser action if the
14790      * key was handled by the KeyMap (defaults to false)
14791      * @type Boolean
14792      */
14793     stopEvent : false,
14794
14795     /**
14796      * Add a new binding to this KeyMap. The following config object properties are supported:
14797      * <pre>
14798 Property    Type             Description
14799 ----------  ---------------  ----------------------------------------------------------------------
14800 key         String/Array     A single keycode or an array of keycodes to handle
14801 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14802 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14803 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14804 fn          Function         The function to call when KeyMap finds the expected key combination
14805 scope       Object           The scope of the callback function
14806 </pre>
14807      *
14808      * Usage:
14809      * <pre><code>
14810 // Create a KeyMap
14811 var map = new Roo.KeyMap(document, {
14812     key: Roo.EventObject.ENTER,
14813     fn: handleKey,
14814     scope: this
14815 });
14816
14817 //Add a new binding to the existing KeyMap later
14818 map.addBinding({
14819     key: 'abc',
14820     shift: true,
14821     fn: handleKey,
14822     scope: this
14823 });
14824 </code></pre>
14825      * @param {Object/Array} config A single KeyMap config or an array of configs
14826      */
14827         addBinding : function(config){
14828         if(config instanceof Array){
14829             for(var i = 0, len = config.length; i < len; i++){
14830                 this.addBinding(config[i]);
14831             }
14832             return;
14833         }
14834         var keyCode = config.key,
14835             shift = config.shift, 
14836             ctrl = config.ctrl, 
14837             alt = config.alt,
14838             fn = config.fn,
14839             scope = config.scope;
14840         if(typeof keyCode == "string"){
14841             var ks = [];
14842             var keyString = keyCode.toUpperCase();
14843             for(var j = 0, len = keyString.length; j < len; j++){
14844                 ks.push(keyString.charCodeAt(j));
14845             }
14846             keyCode = ks;
14847         }
14848         var keyArray = keyCode instanceof Array;
14849         var handler = function(e){
14850             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14851                 var k = e.getKey();
14852                 if(keyArray){
14853                     for(var i = 0, len = keyCode.length; i < len; i++){
14854                         if(keyCode[i] == k){
14855                           if(this.stopEvent){
14856                               e.stopEvent();
14857                           }
14858                           fn.call(scope || window, k, e);
14859                           return;
14860                         }
14861                     }
14862                 }else{
14863                     if(k == keyCode){
14864                         if(this.stopEvent){
14865                            e.stopEvent();
14866                         }
14867                         fn.call(scope || window, k, e);
14868                     }
14869                 }
14870             }
14871         };
14872         this.bindings.push(handler);  
14873         },
14874
14875     /**
14876      * Shorthand for adding a single key listener
14877      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14878      * following options:
14879      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14880      * @param {Function} fn The function to call
14881      * @param {Object} scope (optional) The scope of the function
14882      */
14883     on : function(key, fn, scope){
14884         var keyCode, shift, ctrl, alt;
14885         if(typeof key == "object" && !(key instanceof Array)){
14886             keyCode = key.key;
14887             shift = key.shift;
14888             ctrl = key.ctrl;
14889             alt = key.alt;
14890         }else{
14891             keyCode = key;
14892         }
14893         this.addBinding({
14894             key: keyCode,
14895             shift: shift,
14896             ctrl: ctrl,
14897             alt: alt,
14898             fn: fn,
14899             scope: scope
14900         })
14901     },
14902
14903     // private
14904     handleKeyDown : function(e){
14905             if(this.enabled){ //just in case
14906             var b = this.bindings;
14907             for(var i = 0, len = b.length; i < len; i++){
14908                 b[i].call(this, e);
14909             }
14910             }
14911         },
14912         
14913         /**
14914          * Returns true if this KeyMap is enabled
14915          * @return {Boolean} 
14916          */
14917         isEnabled : function(){
14918             return this.enabled;  
14919         },
14920         
14921         /**
14922          * Enables this KeyMap
14923          */
14924         enable: function(){
14925                 if(!this.enabled){
14926                     this.el.on(this.eventName, this.handleKeyDown, this);
14927                     this.enabled = true;
14928                 }
14929         },
14930
14931         /**
14932          * Disable this KeyMap
14933          */
14934         disable: function(){
14935                 if(this.enabled){
14936                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14937                     this.enabled = false;
14938                 }
14939         }
14940 };/*
14941  * Based on:
14942  * Ext JS Library 1.1.1
14943  * Copyright(c) 2006-2007, Ext JS, LLC.
14944  *
14945  * Originally Released Under LGPL - original licence link has changed is not relivant.
14946  *
14947  * Fork - LGPL
14948  * <script type="text/javascript">
14949  */
14950
14951  
14952 /**
14953  * @class Roo.util.TextMetrics
14954  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14955  * wide, in pixels, a given block of text will be.
14956  * @singleton
14957  */
14958 Roo.util.TextMetrics = function(){
14959     var shared;
14960     return {
14961         /**
14962          * Measures the size of the specified text
14963          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14964          * that can affect the size of the rendered text
14965          * @param {String} text The text to measure
14966          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14967          * in order to accurately measure the text height
14968          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14969          */
14970         measure : function(el, text, fixedWidth){
14971             if(!shared){
14972                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14973             }
14974             shared.bind(el);
14975             shared.setFixedWidth(fixedWidth || 'auto');
14976             return shared.getSize(text);
14977         },
14978
14979         /**
14980          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14981          * the overhead of multiple calls to initialize the style properties on each measurement.
14982          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14983          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14984          * in order to accurately measure the text height
14985          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14986          */
14987         createInstance : function(el, fixedWidth){
14988             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14989         }
14990     };
14991 }();
14992
14993  
14994
14995 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14996     var ml = new Roo.Element(document.createElement('div'));
14997     document.body.appendChild(ml.dom);
14998     ml.position('absolute');
14999     ml.setLeftTop(-1000, -1000);
15000     ml.hide();
15001
15002     if(fixedWidth){
15003         ml.setWidth(fixedWidth);
15004     }
15005      
15006     var instance = {
15007         /**
15008          * Returns the size of the specified text based on the internal element's style and width properties
15009          * @memberOf Roo.util.TextMetrics.Instance#
15010          * @param {String} text The text to measure
15011          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15012          */
15013         getSize : function(text){
15014             ml.update(text);
15015             var s = ml.getSize();
15016             ml.update('');
15017             return s;
15018         },
15019
15020         /**
15021          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15022          * that can affect the size of the rendered text
15023          * @memberOf Roo.util.TextMetrics.Instance#
15024          * @param {String/HTMLElement} el The element, dom node or id
15025          */
15026         bind : function(el){
15027             ml.setStyle(
15028                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15029             );
15030         },
15031
15032         /**
15033          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15034          * to set a fixed width in order to accurately measure the text height.
15035          * @memberOf Roo.util.TextMetrics.Instance#
15036          * @param {Number} width The width to set on the element
15037          */
15038         setFixedWidth : function(width){
15039             ml.setWidth(width);
15040         },
15041
15042         /**
15043          * Returns the measured width of the specified text
15044          * @memberOf Roo.util.TextMetrics.Instance#
15045          * @param {String} text The text to measure
15046          * @return {Number} width The width in pixels
15047          */
15048         getWidth : function(text){
15049             ml.dom.style.width = 'auto';
15050             return this.getSize(text).width;
15051         },
15052
15053         /**
15054          * Returns the measured height of the specified text.  For multiline text, be sure to call
15055          * {@link #setFixedWidth} if necessary.
15056          * @memberOf Roo.util.TextMetrics.Instance#
15057          * @param {String} text The text to measure
15058          * @return {Number} height The height in pixels
15059          */
15060         getHeight : function(text){
15061             return this.getSize(text).height;
15062         }
15063     };
15064
15065     instance.bind(bindTo);
15066
15067     return instance;
15068 };
15069
15070 // backwards compat
15071 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15072  * Based on:
15073  * Ext JS Library 1.1.1
15074  * Copyright(c) 2006-2007, Ext JS, LLC.
15075  *
15076  * Originally Released Under LGPL - original licence link has changed is not relivant.
15077  *
15078  * Fork - LGPL
15079  * <script type="text/javascript">
15080  */
15081
15082 /**
15083  * @class Roo.state.Provider
15084  * Abstract base class for state provider implementations. This class provides methods
15085  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15086  * Provider interface.
15087  */
15088 Roo.state.Provider = function(){
15089     /**
15090      * @event statechange
15091      * Fires when a state change occurs.
15092      * @param {Provider} this This state provider
15093      * @param {String} key The state key which was changed
15094      * @param {String} value The encoded value for the state
15095      */
15096     this.addEvents({
15097         "statechange": true
15098     });
15099     this.state = {};
15100     Roo.state.Provider.superclass.constructor.call(this);
15101 };
15102 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15103     /**
15104      * Returns the current value for a key
15105      * @param {String} name The key name
15106      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15107      * @return {Mixed} The state data
15108      */
15109     get : function(name, defaultValue){
15110         return typeof this.state[name] == "undefined" ?
15111             defaultValue : this.state[name];
15112     },
15113     
15114     /**
15115      * Clears a value from the state
15116      * @param {String} name The key name
15117      */
15118     clear : function(name){
15119         delete this.state[name];
15120         this.fireEvent("statechange", this, name, null);
15121     },
15122     
15123     /**
15124      * Sets the value for a key
15125      * @param {String} name The key name
15126      * @param {Mixed} value The value to set
15127      */
15128     set : function(name, value){
15129         this.state[name] = value;
15130         this.fireEvent("statechange", this, name, value);
15131     },
15132     
15133     /**
15134      * Decodes a string previously encoded with {@link #encodeValue}.
15135      * @param {String} value The value to decode
15136      * @return {Mixed} The decoded value
15137      */
15138     decodeValue : function(cookie){
15139         var re = /^(a|n|d|b|s|o)\:(.*)$/;
15140         var matches = re.exec(unescape(cookie));
15141         if(!matches || !matches[1]) {
15142             return; // non state cookie
15143         }
15144         var type = matches[1];
15145         var v = matches[2];
15146         switch(type){
15147             case "n":
15148                 return parseFloat(v);
15149             case "d":
15150                 return new Date(Date.parse(v));
15151             case "b":
15152                 return (v == "1");
15153             case "a":
15154                 var all = [];
15155                 var values = v.split("^");
15156                 for(var i = 0, len = values.length; i < len; i++){
15157                     all.push(this.decodeValue(values[i]));
15158                 }
15159                 return all;
15160            case "o":
15161                 var all = {};
15162                 var values = v.split("^");
15163                 for(var i = 0, len = values.length; i < len; i++){
15164                     var kv = values[i].split("=");
15165                     all[kv[0]] = this.decodeValue(kv[1]);
15166                 }
15167                 return all;
15168            default:
15169                 return v;
15170         }
15171     },
15172     
15173     /**
15174      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15175      * @param {Mixed} value The value to encode
15176      * @return {String} The encoded value
15177      */
15178     encodeValue : function(v){
15179         var enc;
15180         if(typeof v == "number"){
15181             enc = "n:" + v;
15182         }else if(typeof v == "boolean"){
15183             enc = "b:" + (v ? "1" : "0");
15184         }else if(v instanceof Date){
15185             enc = "d:" + v.toGMTString();
15186         }else if(v instanceof Array){
15187             var flat = "";
15188             for(var i = 0, len = v.length; i < len; i++){
15189                 flat += this.encodeValue(v[i]);
15190                 if(i != len-1) {
15191                     flat += "^";
15192                 }
15193             }
15194             enc = "a:" + flat;
15195         }else if(typeof v == "object"){
15196             var flat = "";
15197             for(var key in v){
15198                 if(typeof v[key] != "function"){
15199                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15200                 }
15201             }
15202             enc = "o:" + flat.substring(0, flat.length-1);
15203         }else{
15204             enc = "s:" + v;
15205         }
15206         return escape(enc);        
15207     }
15208 });
15209
15210 /*
15211  * Based on:
15212  * Ext JS Library 1.1.1
15213  * Copyright(c) 2006-2007, Ext JS, LLC.
15214  *
15215  * Originally Released Under LGPL - original licence link has changed is not relivant.
15216  *
15217  * Fork - LGPL
15218  * <script type="text/javascript">
15219  */
15220 /**
15221  * @class Roo.state.Manager
15222  * This is the global state manager. By default all components that are "state aware" check this class
15223  * for state information if you don't pass them a custom state provider. In order for this class
15224  * to be useful, it must be initialized with a provider when your application initializes.
15225  <pre><code>
15226 // in your initialization function
15227 init : function(){
15228    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15229    ...
15230    // supposed you have a {@link Roo.BorderLayout}
15231    var layout = new Roo.BorderLayout(...);
15232    layout.restoreState();
15233    // or a {Roo.BasicDialog}
15234    var dialog = new Roo.BasicDialog(...);
15235    dialog.restoreState();
15236  </code></pre>
15237  * @singleton
15238  */
15239 Roo.state.Manager = function(){
15240     var provider = new Roo.state.Provider();
15241     
15242     return {
15243         /**
15244          * Configures the default state provider for your application
15245          * @param {Provider} stateProvider The state provider to set
15246          */
15247         setProvider : function(stateProvider){
15248             provider = stateProvider;
15249         },
15250         
15251         /**
15252          * Returns the current value for a key
15253          * @param {String} name The key name
15254          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15255          * @return {Mixed} The state data
15256          */
15257         get : function(key, defaultValue){
15258             return provider.get(key, defaultValue);
15259         },
15260         
15261         /**
15262          * Sets the value for a key
15263          * @param {String} name The key name
15264          * @param {Mixed} value The state data
15265          */
15266          set : function(key, value){
15267             provider.set(key, value);
15268         },
15269         
15270         /**
15271          * Clears a value from the state
15272          * @param {String} name The key name
15273          */
15274         clear : function(key){
15275             provider.clear(key);
15276         },
15277         
15278         /**
15279          * Gets the currently configured state provider
15280          * @return {Provider} The state provider
15281          */
15282         getProvider : function(){
15283             return provider;
15284         }
15285     };
15286 }();
15287 /*
15288  * Based on:
15289  * Ext JS Library 1.1.1
15290  * Copyright(c) 2006-2007, Ext JS, LLC.
15291  *
15292  * Originally Released Under LGPL - original licence link has changed is not relivant.
15293  *
15294  * Fork - LGPL
15295  * <script type="text/javascript">
15296  */
15297 /**
15298  * @class Roo.state.CookieProvider
15299  * @extends Roo.state.Provider
15300  * The default Provider implementation which saves state via cookies.
15301  * <br />Usage:
15302  <pre><code>
15303    var cp = new Roo.state.CookieProvider({
15304        path: "/cgi-bin/",
15305        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15306        domain: "roojs.com"
15307    })
15308    Roo.state.Manager.setProvider(cp);
15309  </code></pre>
15310  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15311  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15312  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15313  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15314  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15315  * domain the page is running on including the 'www' like 'www.roojs.com')
15316  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15317  * @constructor
15318  * Create a new CookieProvider
15319  * @param {Object} config The configuration object
15320  */
15321 Roo.state.CookieProvider = function(config){
15322     Roo.state.CookieProvider.superclass.constructor.call(this);
15323     this.path = "/";
15324     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15325     this.domain = null;
15326     this.secure = false;
15327     Roo.apply(this, config);
15328     this.state = this.readCookies();
15329 };
15330
15331 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15332     // private
15333     set : function(name, value){
15334         if(typeof value == "undefined" || value === null){
15335             this.clear(name);
15336             return;
15337         }
15338         this.setCookie(name, value);
15339         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15340     },
15341
15342     // private
15343     clear : function(name){
15344         this.clearCookie(name);
15345         Roo.state.CookieProvider.superclass.clear.call(this, name);
15346     },
15347
15348     // private
15349     readCookies : function(){
15350         var cookies = {};
15351         var c = document.cookie + ";";
15352         var re = /\s?(.*?)=(.*?);/g;
15353         var matches;
15354         while((matches = re.exec(c)) != null){
15355             var name = matches[1];
15356             var value = matches[2];
15357             if(name && name.substring(0,3) == "ys-"){
15358                 cookies[name.substr(3)] = this.decodeValue(value);
15359             }
15360         }
15361         return cookies;
15362     },
15363
15364     // private
15365     setCookie : function(name, value){
15366         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15367            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15368            ((this.path == null) ? "" : ("; path=" + this.path)) +
15369            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15370            ((this.secure == true) ? "; secure" : "");
15371     },
15372
15373     // private
15374     clearCookie : function(name){
15375         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15376            ((this.path == null) ? "" : ("; path=" + this.path)) +
15377            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15378            ((this.secure == true) ? "; secure" : "");
15379     }
15380 });/*
15381  * Based on:
15382  * Ext JS Library 1.1.1
15383  * Copyright(c) 2006-2007, Ext JS, LLC.
15384  *
15385  * Originally Released Under LGPL - original licence link has changed is not relivant.
15386  *
15387  * Fork - LGPL
15388  * <script type="text/javascript">
15389  */
15390  
15391
15392 /**
15393  * @class Roo.ComponentMgr
15394  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15395  * @singleton
15396  */
15397 Roo.ComponentMgr = function(){
15398     var all = new Roo.util.MixedCollection();
15399
15400     return {
15401         /**
15402          * Registers a component.
15403          * @param {Roo.Component} c The component
15404          */
15405         register : function(c){
15406             all.add(c);
15407         },
15408
15409         /**
15410          * Unregisters a component.
15411          * @param {Roo.Component} c The component
15412          */
15413         unregister : function(c){
15414             all.remove(c);
15415         },
15416
15417         /**
15418          * Returns a component by id
15419          * @param {String} id The component id
15420          */
15421         get : function(id){
15422             return all.get(id);
15423         },
15424
15425         /**
15426          * Registers a function that will be called when a specified component is added to ComponentMgr
15427          * @param {String} id The component id
15428          * @param {Funtction} fn The callback function
15429          * @param {Object} scope The scope of the callback
15430          */
15431         onAvailable : function(id, fn, scope){
15432             all.on("add", function(index, o){
15433                 if(o.id == id){
15434                     fn.call(scope || o, o);
15435                     all.un("add", fn, scope);
15436                 }
15437             });
15438         }
15439     };
15440 }();/*
15441  * Based on:
15442  * Ext JS Library 1.1.1
15443  * Copyright(c) 2006-2007, Ext JS, LLC.
15444  *
15445  * Originally Released Under LGPL - original licence link has changed is not relivant.
15446  *
15447  * Fork - LGPL
15448  * <script type="text/javascript">
15449  */
15450  
15451 /**
15452  * @class Roo.Component
15453  * @extends Roo.util.Observable
15454  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15455  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15456  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15457  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15458  * All visual components (widgets) that require rendering into a layout should subclass Component.
15459  * @constructor
15460  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15461  * 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
15462  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15463  */
15464 Roo.Component = function(config){
15465     config = config || {};
15466     if(config.tagName || config.dom || typeof config == "string"){ // element object
15467         config = {el: config, id: config.id || config};
15468     }
15469     this.initialConfig = config;
15470
15471     Roo.apply(this, config);
15472     this.addEvents({
15473         /**
15474          * @event disable
15475          * Fires after the component is disabled.
15476              * @param {Roo.Component} this
15477              */
15478         disable : true,
15479         /**
15480          * @event enable
15481          * Fires after the component is enabled.
15482              * @param {Roo.Component} this
15483              */
15484         enable : true,
15485         /**
15486          * @event beforeshow
15487          * Fires before the component is shown.  Return false to stop the show.
15488              * @param {Roo.Component} this
15489              */
15490         beforeshow : true,
15491         /**
15492          * @event show
15493          * Fires after the component is shown.
15494              * @param {Roo.Component} this
15495              */
15496         show : true,
15497         /**
15498          * @event beforehide
15499          * Fires before the component is hidden. Return false to stop the hide.
15500              * @param {Roo.Component} this
15501              */
15502         beforehide : true,
15503         /**
15504          * @event hide
15505          * Fires after the component is hidden.
15506              * @param {Roo.Component} this
15507              */
15508         hide : true,
15509         /**
15510          * @event beforerender
15511          * Fires before the component is rendered. Return false to stop the render.
15512              * @param {Roo.Component} this
15513              */
15514         beforerender : true,
15515         /**
15516          * @event render
15517          * Fires after the component is rendered.
15518              * @param {Roo.Component} this
15519              */
15520         render : true,
15521         /**
15522          * @event beforedestroy
15523          * Fires before the component is destroyed. Return false to stop the destroy.
15524              * @param {Roo.Component} this
15525              */
15526         beforedestroy : true,
15527         /**
15528          * @event destroy
15529          * Fires after the component is destroyed.
15530              * @param {Roo.Component} this
15531              */
15532         destroy : true
15533     });
15534     if(!this.id){
15535         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15536     }
15537     Roo.ComponentMgr.register(this);
15538     Roo.Component.superclass.constructor.call(this);
15539     this.initComponent();
15540     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15541         this.render(this.renderTo);
15542         delete this.renderTo;
15543     }
15544 };
15545
15546 /** @private */
15547 Roo.Component.AUTO_ID = 1000;
15548
15549 Roo.extend(Roo.Component, Roo.util.Observable, {
15550     /**
15551      * @scope Roo.Component.prototype
15552      * @type {Boolean}
15553      * true if this component is hidden. Read-only.
15554      */
15555     hidden : false,
15556     /**
15557      * @type {Boolean}
15558      * true if this component is disabled. Read-only.
15559      */
15560     disabled : false,
15561     /**
15562      * @type {Boolean}
15563      * true if this component has been rendered. Read-only.
15564      */
15565     rendered : false,
15566     
15567     /** @cfg {String} disableClass
15568      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15569      */
15570     disabledClass : "x-item-disabled",
15571         /** @cfg {Boolean} allowDomMove
15572          * Whether the component can move the Dom node when rendering (defaults to true).
15573          */
15574     allowDomMove : true,
15575     /** @cfg {String} hideMode (display|visibility)
15576      * How this component should hidden. Supported values are
15577      * "visibility" (css visibility), "offsets" (negative offset position) and
15578      * "display" (css display) - defaults to "display".
15579      */
15580     hideMode: 'display',
15581
15582     /** @private */
15583     ctype : "Roo.Component",
15584
15585     /**
15586      * @cfg {String} actionMode 
15587      * which property holds the element that used for  hide() / show() / disable() / enable()
15588      * default is 'el' for forms you probably want to set this to fieldEl 
15589      */
15590     actionMode : "el",
15591
15592     /** @private */
15593     getActionEl : function(){
15594         return this[this.actionMode];
15595     },
15596
15597     initComponent : Roo.emptyFn,
15598     /**
15599      * If this is a lazy rendering component, render it to its container element.
15600      * @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.
15601      */
15602     render : function(container, position){
15603         
15604         if(this.rendered){
15605             return this;
15606         }
15607         
15608         if(this.fireEvent("beforerender", this) === false){
15609             return false;
15610         }
15611         
15612         if(!container && this.el){
15613             this.el = Roo.get(this.el);
15614             container = this.el.dom.parentNode;
15615             this.allowDomMove = false;
15616         }
15617         this.container = Roo.get(container);
15618         this.rendered = true;
15619         if(position !== undefined){
15620             if(typeof position == 'number'){
15621                 position = this.container.dom.childNodes[position];
15622             }else{
15623                 position = Roo.getDom(position);
15624             }
15625         }
15626         this.onRender(this.container, position || null);
15627         if(this.cls){
15628             this.el.addClass(this.cls);
15629             delete this.cls;
15630         }
15631         if(this.style){
15632             this.el.applyStyles(this.style);
15633             delete this.style;
15634         }
15635         this.fireEvent("render", this);
15636         this.afterRender(this.container);
15637         if(this.hidden){
15638             this.hide();
15639         }
15640         if(this.disabled){
15641             this.disable();
15642         }
15643
15644         return this;
15645         
15646     },
15647
15648     /** @private */
15649     // default function is not really useful
15650     onRender : function(ct, position){
15651         if(this.el){
15652             this.el = Roo.get(this.el);
15653             if(this.allowDomMove !== false){
15654                 ct.dom.insertBefore(this.el.dom, position);
15655             }
15656         }
15657     },
15658
15659     /** @private */
15660     getAutoCreate : function(){
15661         var cfg = typeof this.autoCreate == "object" ?
15662                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15663         if(this.id && !cfg.id){
15664             cfg.id = this.id;
15665         }
15666         return cfg;
15667     },
15668
15669     /** @private */
15670     afterRender : Roo.emptyFn,
15671
15672     /**
15673      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15674      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15675      */
15676     destroy : function(){
15677         if(this.fireEvent("beforedestroy", this) !== false){
15678             this.purgeListeners();
15679             this.beforeDestroy();
15680             if(this.rendered){
15681                 this.el.removeAllListeners();
15682                 this.el.remove();
15683                 if(this.actionMode == "container"){
15684                     this.container.remove();
15685                 }
15686             }
15687             this.onDestroy();
15688             Roo.ComponentMgr.unregister(this);
15689             this.fireEvent("destroy", this);
15690         }
15691     },
15692
15693         /** @private */
15694     beforeDestroy : function(){
15695
15696     },
15697
15698         /** @private */
15699         onDestroy : function(){
15700
15701     },
15702
15703     /**
15704      * Returns the underlying {@link Roo.Element}.
15705      * @return {Roo.Element} The element
15706      */
15707     getEl : function(){
15708         return this.el;
15709     },
15710
15711     /**
15712      * Returns the id of this component.
15713      * @return {String}
15714      */
15715     getId : function(){
15716         return this.id;
15717     },
15718
15719     /**
15720      * Try to focus this component.
15721      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15722      * @return {Roo.Component} this
15723      */
15724     focus : function(selectText){
15725         if(this.rendered){
15726             this.el.focus();
15727             if(selectText === true){
15728                 this.el.dom.select();
15729             }
15730         }
15731         return this;
15732     },
15733
15734     /** @private */
15735     blur : function(){
15736         if(this.rendered){
15737             this.el.blur();
15738         }
15739         return this;
15740     },
15741
15742     /**
15743      * Disable this component.
15744      * @return {Roo.Component} this
15745      */
15746     disable : function(){
15747         if(this.rendered){
15748             this.onDisable();
15749         }
15750         this.disabled = true;
15751         this.fireEvent("disable", this);
15752         return this;
15753     },
15754
15755         // private
15756     onDisable : function(){
15757         this.getActionEl().addClass(this.disabledClass);
15758         this.el.dom.disabled = true;
15759     },
15760
15761     /**
15762      * Enable this component.
15763      * @return {Roo.Component} this
15764      */
15765     enable : function(){
15766         if(this.rendered){
15767             this.onEnable();
15768         }
15769         this.disabled = false;
15770         this.fireEvent("enable", this);
15771         return this;
15772     },
15773
15774         // private
15775     onEnable : function(){
15776         this.getActionEl().removeClass(this.disabledClass);
15777         this.el.dom.disabled = false;
15778     },
15779
15780     /**
15781      * Convenience function for setting disabled/enabled by boolean.
15782      * @param {Boolean} disabled
15783      */
15784     setDisabled : function(disabled){
15785         this[disabled ? "disable" : "enable"]();
15786     },
15787
15788     /**
15789      * Show this component.
15790      * @return {Roo.Component} this
15791      */
15792     show: function(){
15793         if(this.fireEvent("beforeshow", this) !== false){
15794             this.hidden = false;
15795             if(this.rendered){
15796                 this.onShow();
15797             }
15798             this.fireEvent("show", this);
15799         }
15800         return this;
15801     },
15802
15803     // private
15804     onShow : function(){
15805         var ae = this.getActionEl();
15806         if(this.hideMode == 'visibility'){
15807             ae.dom.style.visibility = "visible";
15808         }else if(this.hideMode == 'offsets'){
15809             ae.removeClass('x-hidden');
15810         }else{
15811             ae.dom.style.display = "";
15812         }
15813     },
15814
15815     /**
15816      * Hide this component.
15817      * @return {Roo.Component} this
15818      */
15819     hide: function(){
15820         if(this.fireEvent("beforehide", this) !== false){
15821             this.hidden = true;
15822             if(this.rendered){
15823                 this.onHide();
15824             }
15825             this.fireEvent("hide", this);
15826         }
15827         return this;
15828     },
15829
15830     // private
15831     onHide : function(){
15832         var ae = this.getActionEl();
15833         if(this.hideMode == 'visibility'){
15834             ae.dom.style.visibility = "hidden";
15835         }else if(this.hideMode == 'offsets'){
15836             ae.addClass('x-hidden');
15837         }else{
15838             ae.dom.style.display = "none";
15839         }
15840     },
15841
15842     /**
15843      * Convenience function to hide or show this component by boolean.
15844      * @param {Boolean} visible True to show, false to hide
15845      * @return {Roo.Component} this
15846      */
15847     setVisible: function(visible){
15848         if(visible) {
15849             this.show();
15850         }else{
15851             this.hide();
15852         }
15853         return this;
15854     },
15855
15856     /**
15857      * Returns true if this component is visible.
15858      */
15859     isVisible : function(){
15860         return this.getActionEl().isVisible();
15861     },
15862
15863     cloneConfig : function(overrides){
15864         overrides = overrides || {};
15865         var id = overrides.id || Roo.id();
15866         var cfg = Roo.applyIf(overrides, this.initialConfig);
15867         cfg.id = id; // prevent dup id
15868         return new this.constructor(cfg);
15869     }
15870 });/*
15871  * Based on:
15872  * Ext JS Library 1.1.1
15873  * Copyright(c) 2006-2007, Ext JS, LLC.
15874  *
15875  * Originally Released Under LGPL - original licence link has changed is not relivant.
15876  *
15877  * Fork - LGPL
15878  * <script type="text/javascript">
15879  */
15880
15881 /**
15882  * @class Roo.BoxComponent
15883  * @extends Roo.Component
15884  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15885  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15886  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15887  * layout containers.
15888  * @constructor
15889  * @param {Roo.Element/String/Object} config The configuration options.
15890  */
15891 Roo.BoxComponent = function(config){
15892     Roo.Component.call(this, config);
15893     this.addEvents({
15894         /**
15895          * @event resize
15896          * Fires after the component is resized.
15897              * @param {Roo.Component} this
15898              * @param {Number} adjWidth The box-adjusted width that was set
15899              * @param {Number} adjHeight The box-adjusted height that was set
15900              * @param {Number} rawWidth The width that was originally specified
15901              * @param {Number} rawHeight The height that was originally specified
15902              */
15903         resize : true,
15904         /**
15905          * @event move
15906          * Fires after the component is moved.
15907              * @param {Roo.Component} this
15908              * @param {Number} x The new x position
15909              * @param {Number} y The new y position
15910              */
15911         move : true
15912     });
15913 };
15914
15915 Roo.extend(Roo.BoxComponent, Roo.Component, {
15916     // private, set in afterRender to signify that the component has been rendered
15917     boxReady : false,
15918     // private, used to defer height settings to subclasses
15919     deferHeight: false,
15920     /** @cfg {Number} width
15921      * width (optional) size of component
15922      */
15923      /** @cfg {Number} height
15924      * height (optional) size of component
15925      */
15926      
15927     /**
15928      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15929      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15930      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15931      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15932      * @return {Roo.BoxComponent} this
15933      */
15934     setSize : function(w, h){
15935         // support for standard size objects
15936         if(typeof w == 'object'){
15937             h = w.height;
15938             w = w.width;
15939         }
15940         // not rendered
15941         if(!this.boxReady){
15942             this.width = w;
15943             this.height = h;
15944             return this;
15945         }
15946
15947         // prevent recalcs when not needed
15948         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15949             return this;
15950         }
15951         this.lastSize = {width: w, height: h};
15952
15953         var adj = this.adjustSize(w, h);
15954         var aw = adj.width, ah = adj.height;
15955         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15956             var rz = this.getResizeEl();
15957             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15958                 rz.setSize(aw, ah);
15959             }else if(!this.deferHeight && ah !== undefined){
15960                 rz.setHeight(ah);
15961             }else if(aw !== undefined){
15962                 rz.setWidth(aw);
15963             }
15964             this.onResize(aw, ah, w, h);
15965             this.fireEvent('resize', this, aw, ah, w, h);
15966         }
15967         return this;
15968     },
15969
15970     /**
15971      * Gets the current size of the component's underlying element.
15972      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15973      */
15974     getSize : function(){
15975         return this.el.getSize();
15976     },
15977
15978     /**
15979      * Gets the current XY position of the component's underlying element.
15980      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15981      * @return {Array} The XY position of the element (e.g., [100, 200])
15982      */
15983     getPosition : function(local){
15984         if(local === true){
15985             return [this.el.getLeft(true), this.el.getTop(true)];
15986         }
15987         return this.xy || this.el.getXY();
15988     },
15989
15990     /**
15991      * Gets the current box measurements of the component's underlying element.
15992      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15993      * @returns {Object} box An object in the format {x, y, width, height}
15994      */
15995     getBox : function(local){
15996         var s = this.el.getSize();
15997         if(local){
15998             s.x = this.el.getLeft(true);
15999             s.y = this.el.getTop(true);
16000         }else{
16001             var xy = this.xy || this.el.getXY();
16002             s.x = xy[0];
16003             s.y = xy[1];
16004         }
16005         return s;
16006     },
16007
16008     /**
16009      * Sets the current box measurements of the component's underlying element.
16010      * @param {Object} box An object in the format {x, y, width, height}
16011      * @returns {Roo.BoxComponent} this
16012      */
16013     updateBox : function(box){
16014         this.setSize(box.width, box.height);
16015         this.setPagePosition(box.x, box.y);
16016         return this;
16017     },
16018
16019     // protected
16020     getResizeEl : function(){
16021         return this.resizeEl || this.el;
16022     },
16023
16024     // protected
16025     getPositionEl : function(){
16026         return this.positionEl || this.el;
16027     },
16028
16029     /**
16030      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16031      * This method fires the move event.
16032      * @param {Number} left The new left
16033      * @param {Number} top The new top
16034      * @returns {Roo.BoxComponent} this
16035      */
16036     setPosition : function(x, y){
16037         this.x = x;
16038         this.y = y;
16039         if(!this.boxReady){
16040             return this;
16041         }
16042         var adj = this.adjustPosition(x, y);
16043         var ax = adj.x, ay = adj.y;
16044
16045         var el = this.getPositionEl();
16046         if(ax !== undefined || ay !== undefined){
16047             if(ax !== undefined && ay !== undefined){
16048                 el.setLeftTop(ax, ay);
16049             }else if(ax !== undefined){
16050                 el.setLeft(ax);
16051             }else if(ay !== undefined){
16052                 el.setTop(ay);
16053             }
16054             this.onPosition(ax, ay);
16055             this.fireEvent('move', this, ax, ay);
16056         }
16057         return this;
16058     },
16059
16060     /**
16061      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16062      * This method fires the move event.
16063      * @param {Number} x The new x position
16064      * @param {Number} y The new y position
16065      * @returns {Roo.BoxComponent} this
16066      */
16067     setPagePosition : function(x, y){
16068         this.pageX = x;
16069         this.pageY = y;
16070         if(!this.boxReady){
16071             return;
16072         }
16073         if(x === undefined || y === undefined){ // cannot translate undefined points
16074             return;
16075         }
16076         var p = this.el.translatePoints(x, y);
16077         this.setPosition(p.left, p.top);
16078         return this;
16079     },
16080
16081     // private
16082     onRender : function(ct, position){
16083         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16084         if(this.resizeEl){
16085             this.resizeEl = Roo.get(this.resizeEl);
16086         }
16087         if(this.positionEl){
16088             this.positionEl = Roo.get(this.positionEl);
16089         }
16090     },
16091
16092     // private
16093     afterRender : function(){
16094         Roo.BoxComponent.superclass.afterRender.call(this);
16095         this.boxReady = true;
16096         this.setSize(this.width, this.height);
16097         if(this.x || this.y){
16098             this.setPosition(this.x, this.y);
16099         }
16100         if(this.pageX || this.pageY){
16101             this.setPagePosition(this.pageX, this.pageY);
16102         }
16103     },
16104
16105     /**
16106      * Force the component's size to recalculate based on the underlying element's current height and width.
16107      * @returns {Roo.BoxComponent} this
16108      */
16109     syncSize : function(){
16110         delete this.lastSize;
16111         this.setSize(this.el.getWidth(), this.el.getHeight());
16112         return this;
16113     },
16114
16115     /**
16116      * Called after the component is resized, this method is empty by default but can be implemented by any
16117      * subclass that needs to perform custom logic after a resize occurs.
16118      * @param {Number} adjWidth The box-adjusted width that was set
16119      * @param {Number} adjHeight The box-adjusted height that was set
16120      * @param {Number} rawWidth The width that was originally specified
16121      * @param {Number} rawHeight The height that was originally specified
16122      */
16123     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
16124
16125     },
16126
16127     /**
16128      * Called after the component is moved, this method is empty by default but can be implemented by any
16129      * subclass that needs to perform custom logic after a move occurs.
16130      * @param {Number} x The new x position
16131      * @param {Number} y The new y position
16132      */
16133     onPosition : function(x, y){
16134
16135     },
16136
16137     // private
16138     adjustSize : function(w, h){
16139         if(this.autoWidth){
16140             w = 'auto';
16141         }
16142         if(this.autoHeight){
16143             h = 'auto';
16144         }
16145         return {width : w, height: h};
16146     },
16147
16148     // private
16149     adjustPosition : function(x, y){
16150         return {x : x, y: y};
16151     }
16152 });/*
16153  * Based on:
16154  * Ext JS Library 1.1.1
16155  * Copyright(c) 2006-2007, Ext JS, LLC.
16156  *
16157  * Originally Released Under LGPL - original licence link has changed is not relivant.
16158  *
16159  * Fork - LGPL
16160  * <script type="text/javascript">
16161  */
16162  (function(){ 
16163 /**
16164  * @class Roo.Layer
16165  * @extends Roo.Element
16166  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
16167  * automatic maintaining of shadow/shim positions.
16168  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16169  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
16170  * you can pass a string with a CSS class name. False turns off the shadow.
16171  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
16172  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16173  * @cfg {String} cls CSS class to add to the element
16174  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16175  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
16176  * @constructor
16177  * @param {Object} config An object with config options.
16178  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16179  */
16180
16181 Roo.Layer = function(config, existingEl){
16182     config = config || {};
16183     var dh = Roo.DomHelper;
16184     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
16185     if(existingEl){
16186         this.dom = Roo.getDom(existingEl);
16187     }
16188     if(!this.dom){
16189         var o = config.dh || {tag: "div", cls: "x-layer"};
16190         this.dom = dh.append(pel, o);
16191     }
16192     if(config.cls){
16193         this.addClass(config.cls);
16194     }
16195     this.constrain = config.constrain !== false;
16196     this.visibilityMode = Roo.Element.VISIBILITY;
16197     if(config.id){
16198         this.id = this.dom.id = config.id;
16199     }else{
16200         this.id = Roo.id(this.dom);
16201     }
16202     this.zindex = config.zindex || this.getZIndex();
16203     this.position("absolute", this.zindex);
16204     if(config.shadow){
16205         this.shadowOffset = config.shadowOffset || 4;
16206         this.shadow = new Roo.Shadow({
16207             offset : this.shadowOffset,
16208             mode : config.shadow
16209         });
16210     }else{
16211         this.shadowOffset = 0;
16212     }
16213     this.useShim = config.shim !== false && Roo.useShims;
16214     this.useDisplay = config.useDisplay;
16215     this.hide();
16216 };
16217
16218 var supr = Roo.Element.prototype;
16219
16220 // shims are shared among layer to keep from having 100 iframes
16221 var shims = [];
16222
16223 Roo.extend(Roo.Layer, Roo.Element, {
16224
16225     getZIndex : function(){
16226         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
16227     },
16228
16229     getShim : function(){
16230         if(!this.useShim){
16231             return null;
16232         }
16233         if(this.shim){
16234             return this.shim;
16235         }
16236         var shim = shims.shift();
16237         if(!shim){
16238             shim = this.createShim();
16239             shim.enableDisplayMode('block');
16240             shim.dom.style.display = 'none';
16241             shim.dom.style.visibility = 'visible';
16242         }
16243         var pn = this.dom.parentNode;
16244         if(shim.dom.parentNode != pn){
16245             pn.insertBefore(shim.dom, this.dom);
16246         }
16247         shim.setStyle('z-index', this.getZIndex()-2);
16248         this.shim = shim;
16249         return shim;
16250     },
16251
16252     hideShim : function(){
16253         if(this.shim){
16254             this.shim.setDisplayed(false);
16255             shims.push(this.shim);
16256             delete this.shim;
16257         }
16258     },
16259
16260     disableShadow : function(){
16261         if(this.shadow){
16262             this.shadowDisabled = true;
16263             this.shadow.hide();
16264             this.lastShadowOffset = this.shadowOffset;
16265             this.shadowOffset = 0;
16266         }
16267     },
16268
16269     enableShadow : function(show){
16270         if(this.shadow){
16271             this.shadowDisabled = false;
16272             this.shadowOffset = this.lastShadowOffset;
16273             delete this.lastShadowOffset;
16274             if(show){
16275                 this.sync(true);
16276             }
16277         }
16278     },
16279
16280     // private
16281     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
16282     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
16283     sync : function(doShow){
16284         var sw = this.shadow;
16285         if(!this.updating && this.isVisible() && (sw || this.useShim)){
16286             var sh = this.getShim();
16287
16288             var w = this.getWidth(),
16289                 h = this.getHeight();
16290
16291             var l = this.getLeft(true),
16292                 t = this.getTop(true);
16293
16294             if(sw && !this.shadowDisabled){
16295                 if(doShow && !sw.isVisible()){
16296                     sw.show(this);
16297                 }else{
16298                     sw.realign(l, t, w, h);
16299                 }
16300                 if(sh){
16301                     if(doShow){
16302                        sh.show();
16303                     }
16304                     // fit the shim behind the shadow, so it is shimmed too
16305                     var a = sw.adjusts, s = sh.dom.style;
16306                     s.left = (Math.min(l, l+a.l))+"px";
16307                     s.top = (Math.min(t, t+a.t))+"px";
16308                     s.width = (w+a.w)+"px";
16309                     s.height = (h+a.h)+"px";
16310                 }
16311             }else if(sh){
16312                 if(doShow){
16313                    sh.show();
16314                 }
16315                 sh.setSize(w, h);
16316                 sh.setLeftTop(l, t);
16317             }
16318             
16319         }
16320     },
16321
16322     // private
16323     destroy : function(){
16324         this.hideShim();
16325         if(this.shadow){
16326             this.shadow.hide();
16327         }
16328         this.removeAllListeners();
16329         var pn = this.dom.parentNode;
16330         if(pn){
16331             pn.removeChild(this.dom);
16332         }
16333         Roo.Element.uncache(this.id);
16334     },
16335
16336     remove : function(){
16337         this.destroy();
16338     },
16339
16340     // private
16341     beginUpdate : function(){
16342         this.updating = true;
16343     },
16344
16345     // private
16346     endUpdate : function(){
16347         this.updating = false;
16348         this.sync(true);
16349     },
16350
16351     // private
16352     hideUnders : function(negOffset){
16353         if(this.shadow){
16354             this.shadow.hide();
16355         }
16356         this.hideShim();
16357     },
16358
16359     // private
16360     constrainXY : function(){
16361         if(this.constrain){
16362             var vw = Roo.lib.Dom.getViewWidth(),
16363                 vh = Roo.lib.Dom.getViewHeight();
16364             var s = Roo.get(document).getScroll();
16365
16366             var xy = this.getXY();
16367             var x = xy[0], y = xy[1];   
16368             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
16369             // only move it if it needs it
16370             var moved = false;
16371             // first validate right/bottom
16372             if((x + w) > vw+s.left){
16373                 x = vw - w - this.shadowOffset;
16374                 moved = true;
16375             }
16376             if((y + h) > vh+s.top){
16377                 y = vh - h - this.shadowOffset;
16378                 moved = true;
16379             }
16380             // then make sure top/left isn't negative
16381             if(x < s.left){
16382                 x = s.left;
16383                 moved = true;
16384             }
16385             if(y < s.top){
16386                 y = s.top;
16387                 moved = true;
16388             }
16389             if(moved){
16390                 if(this.avoidY){
16391                     var ay = this.avoidY;
16392                     if(y <= ay && (y+h) >= ay){
16393                         y = ay-h-5;   
16394                     }
16395                 }
16396                 xy = [x, y];
16397                 this.storeXY(xy);
16398                 supr.setXY.call(this, xy);
16399                 this.sync();
16400             }
16401         }
16402     },
16403
16404     isVisible : function(){
16405         return this.visible;    
16406     },
16407
16408     // private
16409     showAction : function(){
16410         this.visible = true; // track visibility to prevent getStyle calls
16411         if(this.useDisplay === true){
16412             this.setDisplayed("");
16413         }else if(this.lastXY){
16414             supr.setXY.call(this, this.lastXY);
16415         }else if(this.lastLT){
16416             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
16417         }
16418     },
16419
16420     // private
16421     hideAction : function(){
16422         this.visible = false;
16423         if(this.useDisplay === true){
16424             this.setDisplayed(false);
16425         }else{
16426             this.setLeftTop(-10000,-10000);
16427         }
16428     },
16429
16430     // overridden Element method
16431     setVisible : function(v, a, d, c, e){
16432         if(v){
16433             this.showAction();
16434         }
16435         if(a && v){
16436             var cb = function(){
16437                 this.sync(true);
16438                 if(c){
16439                     c();
16440                 }
16441             }.createDelegate(this);
16442             supr.setVisible.call(this, true, true, d, cb, e);
16443         }else{
16444             if(!v){
16445                 this.hideUnders(true);
16446             }
16447             var cb = c;
16448             if(a){
16449                 cb = function(){
16450                     this.hideAction();
16451                     if(c){
16452                         c();
16453                     }
16454                 }.createDelegate(this);
16455             }
16456             supr.setVisible.call(this, v, a, d, cb, e);
16457             if(v){
16458                 this.sync(true);
16459             }else if(!a){
16460                 this.hideAction();
16461             }
16462         }
16463     },
16464
16465     storeXY : function(xy){
16466         delete this.lastLT;
16467         this.lastXY = xy;
16468     },
16469
16470     storeLeftTop : function(left, top){
16471         delete this.lastXY;
16472         this.lastLT = [left, top];
16473     },
16474
16475     // private
16476     beforeFx : function(){
16477         this.beforeAction();
16478         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
16479     },
16480
16481     // private
16482     afterFx : function(){
16483         Roo.Layer.superclass.afterFx.apply(this, arguments);
16484         this.sync(this.isVisible());
16485     },
16486
16487     // private
16488     beforeAction : function(){
16489         if(!this.updating && this.shadow){
16490             this.shadow.hide();
16491         }
16492     },
16493
16494     // overridden Element method
16495     setLeft : function(left){
16496         this.storeLeftTop(left, this.getTop(true));
16497         supr.setLeft.apply(this, arguments);
16498         this.sync();
16499     },
16500
16501     setTop : function(top){
16502         this.storeLeftTop(this.getLeft(true), top);
16503         supr.setTop.apply(this, arguments);
16504         this.sync();
16505     },
16506
16507     setLeftTop : function(left, top){
16508         this.storeLeftTop(left, top);
16509         supr.setLeftTop.apply(this, arguments);
16510         this.sync();
16511     },
16512
16513     setXY : function(xy, a, d, c, e){
16514         this.fixDisplay();
16515         this.beforeAction();
16516         this.storeXY(xy);
16517         var cb = this.createCB(c);
16518         supr.setXY.call(this, xy, a, d, cb, e);
16519         if(!a){
16520             cb();
16521         }
16522     },
16523
16524     // private
16525     createCB : function(c){
16526         var el = this;
16527         return function(){
16528             el.constrainXY();
16529             el.sync(true);
16530             if(c){
16531                 c();
16532             }
16533         };
16534     },
16535
16536     // overridden Element method
16537     setX : function(x, a, d, c, e){
16538         this.setXY([x, this.getY()], a, d, c, e);
16539     },
16540
16541     // overridden Element method
16542     setY : function(y, a, d, c, e){
16543         this.setXY([this.getX(), y], a, d, c, e);
16544     },
16545
16546     // overridden Element method
16547     setSize : function(w, h, a, d, c, e){
16548         this.beforeAction();
16549         var cb = this.createCB(c);
16550         supr.setSize.call(this, w, h, a, d, cb, e);
16551         if(!a){
16552             cb();
16553         }
16554     },
16555
16556     // overridden Element method
16557     setWidth : function(w, a, d, c, e){
16558         this.beforeAction();
16559         var cb = this.createCB(c);
16560         supr.setWidth.call(this, w, a, d, cb, e);
16561         if(!a){
16562             cb();
16563         }
16564     },
16565
16566     // overridden Element method
16567     setHeight : function(h, a, d, c, e){
16568         this.beforeAction();
16569         var cb = this.createCB(c);
16570         supr.setHeight.call(this, h, a, d, cb, e);
16571         if(!a){
16572             cb();
16573         }
16574     },
16575
16576     // overridden Element method
16577     setBounds : function(x, y, w, h, a, d, c, e){
16578         this.beforeAction();
16579         var cb = this.createCB(c);
16580         if(!a){
16581             this.storeXY([x, y]);
16582             supr.setXY.call(this, [x, y]);
16583             supr.setSize.call(this, w, h, a, d, cb, e);
16584             cb();
16585         }else{
16586             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
16587         }
16588         return this;
16589     },
16590     
16591     /**
16592      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
16593      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
16594      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
16595      * @param {Number} zindex The new z-index to set
16596      * @return {this} The Layer
16597      */
16598     setZIndex : function(zindex){
16599         this.zindex = zindex;
16600         this.setStyle("z-index", zindex + 2);
16601         if(this.shadow){
16602             this.shadow.setZIndex(zindex + 1);
16603         }
16604         if(this.shim){
16605             this.shim.setStyle("z-index", zindex);
16606         }
16607     }
16608 });
16609 })();/*
16610  * Original code for Roojs - LGPL
16611  * <script type="text/javascript">
16612  */
16613  
16614 /**
16615  * @class Roo.XComponent
16616  * A delayed Element creator...
16617  * Or a way to group chunks of interface together.
16618  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16619  *  used in conjunction with XComponent.build() it will create an instance of each element,
16620  *  then call addxtype() to build the User interface.
16621  * 
16622  * Mypart.xyx = new Roo.XComponent({
16623
16624     parent : 'Mypart.xyz', // empty == document.element.!!
16625     order : '001',
16626     name : 'xxxx'
16627     region : 'xxxx'
16628     disabled : function() {} 
16629      
16630     tree : function() { // return an tree of xtype declared components
16631         var MODULE = this;
16632         return 
16633         {
16634             xtype : 'NestedLayoutPanel',
16635             // technicall
16636         }
16637      ]
16638  *})
16639  *
16640  *
16641  * It can be used to build a big heiracy, with parent etc.
16642  * or you can just use this to render a single compoent to a dom element
16643  * MYPART.render(Roo.Element | String(id) | dom_element )
16644  *
16645  *
16646  * Usage patterns.
16647  *
16648  * Classic Roo
16649  *
16650  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16651  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16652  *
16653  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16654  *
16655  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16656  * - if mulitple topModules exist, the last one is defined as the top module.
16657  *
16658  * Embeded Roo
16659  * 
16660  * When the top level or multiple modules are to embedded into a existing HTML page,
16661  * the parent element can container '#id' of the element where the module will be drawn.
16662  *
16663  * Bootstrap Roo
16664  *
16665  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16666  * it relies more on a include mechanism, where sub modules are included into an outer page.
16667  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16668  * 
16669  * Bootstrap Roo Included elements
16670  *
16671  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16672  * hence confusing the component builder as it thinks there are multiple top level elements. 
16673  *
16674  * String Over-ride & Translations
16675  *
16676  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16677  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16678  * are needed. @see Roo.XComponent.overlayString  
16679  * 
16680  * 
16681  * 
16682  * @extends Roo.util.Observable
16683  * @constructor
16684  * @param cfg {Object} configuration of component
16685  * 
16686  */
16687 Roo.XComponent = function(cfg) {
16688     Roo.apply(this, cfg);
16689     this.addEvents({ 
16690         /**
16691              * @event built
16692              * Fires when this the componnt is built
16693              * @param {Roo.XComponent} c the component
16694              */
16695         'built' : true
16696         
16697     });
16698     this.region = this.region || 'center'; // default..
16699     Roo.XComponent.register(this);
16700     this.modules = false;
16701     this.el = false; // where the layout goes..
16702     
16703     
16704 }
16705 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16706     /**
16707      * @property el
16708      * The created element (with Roo.factory())
16709      * @type {Roo.Layout}
16710      */
16711     el  : false,
16712     
16713     /**
16714      * @property el
16715      * for BC  - use el in new code
16716      * @type {Roo.Layout}
16717      */
16718     panel : false,
16719     
16720     /**
16721      * @property layout
16722      * for BC  - use el in new code
16723      * @type {Roo.Layout}
16724      */
16725     layout : false,
16726     
16727      /**
16728      * @cfg {Function|boolean} disabled
16729      * If this module is disabled by some rule, return true from the funtion
16730      */
16731     disabled : false,
16732     
16733     /**
16734      * @cfg {String} parent 
16735      * Name of parent element which it get xtype added to..
16736      */
16737     parent: false,
16738     
16739     /**
16740      * @cfg {String} order
16741      * Used to set the order in which elements are created (usefull for multiple tabs)
16742      */
16743     
16744     order : false,
16745     /**
16746      * @cfg {String} name
16747      * String to display while loading.
16748      */
16749     name : false,
16750     /**
16751      * @cfg {String} region
16752      * Region to render component to (defaults to center)
16753      */
16754     region : 'center',
16755     
16756     /**
16757      * @cfg {Array} items
16758      * A single item array - the first element is the root of the tree..
16759      * It's done this way to stay compatible with the Xtype system...
16760      */
16761     items : false,
16762     
16763     /**
16764      * @property _tree
16765      * The method that retuns the tree of parts that make up this compoennt 
16766      * @type {function}
16767      */
16768     _tree  : false,
16769     
16770      /**
16771      * render
16772      * render element to dom or tree
16773      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16774      */
16775     
16776     render : function(el)
16777     {
16778         
16779         el = el || false;
16780         var hp = this.parent ? 1 : 0;
16781         Roo.debug &&  Roo.log(this);
16782         
16783         var tree = this._tree ? this._tree() : this.tree();
16784
16785         
16786         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16787             // if parent is a '#.....' string, then let's use that..
16788             var ename = this.parent.substr(1);
16789             this.parent = false;
16790             Roo.debug && Roo.log(ename);
16791             switch (ename) {
16792                 case 'bootstrap-body':
16793                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16794                         // this is the BorderLayout standard?
16795                        this.parent = { el : true };
16796                        break;
16797                     }
16798                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16799                         // need to insert stuff...
16800                         this.parent =  {
16801                              el : new Roo.bootstrap.layout.Border({
16802                                  el : document.body, 
16803                      
16804                                  center: {
16805                                     titlebar: false,
16806                                     autoScroll:false,
16807                                     closeOnTab: true,
16808                                     tabPosition: 'top',
16809                                       //resizeTabs: true,
16810                                     alwaysShowTabs: true,
16811                                     hideTabs: false
16812                                      //minTabWidth: 140
16813                                  }
16814                              })
16815                         
16816                          };
16817                          break;
16818                     }
16819                          
16820                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16821                         this.parent = { el :  new  Roo.bootstrap.Body() };
16822                         Roo.debug && Roo.log("setting el to doc body");
16823                          
16824                     } else {
16825                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16826                     }
16827                     break;
16828                 case 'bootstrap':
16829                     this.parent = { el : true};
16830                     // fall through
16831                 default:
16832                     el = Roo.get(ename);
16833                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16834                         this.parent = { el : true};
16835                     }
16836                     
16837                     break;
16838             }
16839                 
16840             
16841             if (!el && !this.parent) {
16842                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16843                 return;
16844             }
16845         }
16846         
16847         Roo.debug && Roo.log("EL:");
16848         Roo.debug && Roo.log(el);
16849         Roo.debug && Roo.log("this.parent.el:");
16850         Roo.debug && Roo.log(this.parent.el);
16851         
16852
16853         // altertive root elements ??? - we need a better way to indicate these.
16854         var is_alt = Roo.XComponent.is_alt ||
16855                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16856                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16857                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16858         
16859         
16860         
16861         if (!this.parent && is_alt) {
16862             //el = Roo.get(document.body);
16863             this.parent = { el : true };
16864         }
16865             
16866             
16867         
16868         if (!this.parent) {
16869             
16870             Roo.debug && Roo.log("no parent - creating one");
16871             
16872             el = el ? Roo.get(el) : false;      
16873             
16874             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16875                 
16876                 this.parent =  {
16877                     el : new Roo.bootstrap.layout.Border({
16878                         el: el || document.body,
16879                     
16880                         center: {
16881                             titlebar: false,
16882                             autoScroll:false,
16883                             closeOnTab: true,
16884                             tabPosition: 'top',
16885                              //resizeTabs: true,
16886                             alwaysShowTabs: false,
16887                             hideTabs: true,
16888                             minTabWidth: 140,
16889                             overflow: 'visible'
16890                          }
16891                      })
16892                 };
16893             } else {
16894             
16895                 // it's a top level one..
16896                 this.parent =  {
16897                     el : new Roo.BorderLayout(el || document.body, {
16898                         center: {
16899                             titlebar: false,
16900                             autoScroll:false,
16901                             closeOnTab: true,
16902                             tabPosition: 'top',
16903                              //resizeTabs: true,
16904                             alwaysShowTabs: el && hp? false :  true,
16905                             hideTabs: el || !hp ? true :  false,
16906                             minTabWidth: 140
16907                          }
16908                     })
16909                 };
16910             }
16911         }
16912         
16913         if (!this.parent.el) {
16914                 // probably an old style ctor, which has been disabled.
16915                 return;
16916
16917         }
16918                 // The 'tree' method is  '_tree now' 
16919             
16920         tree.region = tree.region || this.region;
16921         var is_body = false;
16922         if (this.parent.el === true) {
16923             // bootstrap... - body..
16924             if (el) {
16925                 tree.el = el;
16926             }
16927             this.parent.el = Roo.factory(tree);
16928             is_body = true;
16929         }
16930         
16931         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16932         this.fireEvent('built', this);
16933         
16934         this.panel = this.el;
16935         this.layout = this.panel.layout;
16936         this.parentLayout = this.parent.layout  || false;  
16937          
16938     }
16939     
16940 });
16941
16942 Roo.apply(Roo.XComponent, {
16943     /**
16944      * @property  hideProgress
16945      * true to disable the building progress bar.. usefull on single page renders.
16946      * @type Boolean
16947      */
16948     hideProgress : false,
16949     /**
16950      * @property  buildCompleted
16951      * True when the builder has completed building the interface.
16952      * @type Boolean
16953      */
16954     buildCompleted : false,
16955      
16956     /**
16957      * @property  topModule
16958      * the upper most module - uses document.element as it's constructor.
16959      * @type Object
16960      */
16961      
16962     topModule  : false,
16963       
16964     /**
16965      * @property  modules
16966      * array of modules to be created by registration system.
16967      * @type {Array} of Roo.XComponent
16968      */
16969     
16970     modules : [],
16971     /**
16972      * @property  elmodules
16973      * array of modules to be created by which use #ID 
16974      * @type {Array} of Roo.XComponent
16975      */
16976      
16977     elmodules : [],
16978
16979      /**
16980      * @property  is_alt
16981      * Is an alternative Root - normally used by bootstrap or other systems,
16982      *    where the top element in the tree can wrap 'body' 
16983      * @type {boolean}  (default false)
16984      */
16985      
16986     is_alt : false,
16987     /**
16988      * @property  build_from_html
16989      * Build elements from html - used by bootstrap HTML stuff 
16990      *    - this is cleared after build is completed
16991      * @type {boolean}    (default false)
16992      */
16993      
16994     build_from_html : false,
16995     /**
16996      * Register components to be built later.
16997      *
16998      * This solves the following issues
16999      * - Building is not done on page load, but after an authentication process has occured.
17000      * - Interface elements are registered on page load
17001      * - Parent Interface elements may not be loaded before child, so this handles that..
17002      * 
17003      *
17004      * example:
17005      * 
17006      * MyApp.register({
17007           order : '000001',
17008           module : 'Pman.Tab.projectMgr',
17009           region : 'center',
17010           parent : 'Pman.layout',
17011           disabled : false,  // or use a function..
17012         })
17013      
17014      * * @param {Object} details about module
17015      */
17016     register : function(obj) {
17017                 
17018         Roo.XComponent.event.fireEvent('register', obj);
17019         switch(typeof(obj.disabled) ) {
17020                 
17021             case 'undefined':
17022                 break;
17023             
17024             case 'function':
17025                 if ( obj.disabled() ) {
17026                         return;
17027                 }
17028                 break;
17029             
17030             default:
17031                 if (obj.disabled || obj.region == '#disabled') {
17032                         return;
17033                 }
17034                 break;
17035         }
17036                 
17037         this.modules.push(obj);
17038          
17039     },
17040     /**
17041      * convert a string to an object..
17042      * eg. 'AAA.BBB' -> finds AAA.BBB
17043
17044      */
17045     
17046     toObject : function(str)
17047     {
17048         if (!str || typeof(str) == 'object') {
17049             return str;
17050         }
17051         if (str.substring(0,1) == '#') {
17052             return str;
17053         }
17054
17055         var ar = str.split('.');
17056         var rt, o;
17057         rt = ar.shift();
17058             /** eval:var:o */
17059         try {
17060             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17061         } catch (e) {
17062             throw "Module not found : " + str;
17063         }
17064         
17065         if (o === false) {
17066             throw "Module not found : " + str;
17067         }
17068         Roo.each(ar, function(e) {
17069             if (typeof(o[e]) == 'undefined') {
17070                 throw "Module not found : " + str;
17071             }
17072             o = o[e];
17073         });
17074         
17075         return o;
17076         
17077     },
17078     
17079     
17080     /**
17081      * move modules into their correct place in the tree..
17082      * 
17083      */
17084     preBuild : function ()
17085     {
17086         var _t = this;
17087         Roo.each(this.modules , function (obj)
17088         {
17089             Roo.XComponent.event.fireEvent('beforebuild', obj);
17090             
17091             var opar = obj.parent;
17092             try { 
17093                 obj.parent = this.toObject(opar);
17094             } catch(e) {
17095                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17096                 return;
17097             }
17098             
17099             if (!obj.parent) {
17100                 Roo.debug && Roo.log("GOT top level module");
17101                 Roo.debug && Roo.log(obj);
17102                 obj.modules = new Roo.util.MixedCollection(false, 
17103                     function(o) { return o.order + '' }
17104                 );
17105                 this.topModule = obj;
17106                 return;
17107             }
17108                         // parent is a string (usually a dom element name..)
17109             if (typeof(obj.parent) == 'string') {
17110                 this.elmodules.push(obj);
17111                 return;
17112             }
17113             if (obj.parent.constructor != Roo.XComponent) {
17114                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17115             }
17116             if (!obj.parent.modules) {
17117                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17118                     function(o) { return o.order + '' }
17119                 );
17120             }
17121             if (obj.parent.disabled) {
17122                 obj.disabled = true;
17123             }
17124             obj.parent.modules.add(obj);
17125         }, this);
17126     },
17127     
17128      /**
17129      * make a list of modules to build.
17130      * @return {Array} list of modules. 
17131      */ 
17132     
17133     buildOrder : function()
17134     {
17135         var _this = this;
17136         var cmp = function(a,b) {   
17137             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
17138         };
17139         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
17140             throw "No top level modules to build";
17141         }
17142         
17143         // make a flat list in order of modules to build.
17144         var mods = this.topModule ? [ this.topModule ] : [];
17145                 
17146         
17147         // elmodules (is a list of DOM based modules )
17148         Roo.each(this.elmodules, function(e) {
17149             mods.push(e);
17150             if (!this.topModule &&
17151                 typeof(e.parent) == 'string' &&
17152                 e.parent.substring(0,1) == '#' &&
17153                 Roo.get(e.parent.substr(1))
17154                ) {
17155                 
17156                 _this.topModule = e;
17157             }
17158             
17159         });
17160
17161         
17162         // add modules to their parents..
17163         var addMod = function(m) {
17164             Roo.debug && Roo.log("build Order: add: " + m.name);
17165                 
17166             mods.push(m);
17167             if (m.modules && !m.disabled) {
17168                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
17169                 m.modules.keySort('ASC',  cmp );
17170                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
17171     
17172                 m.modules.each(addMod);
17173             } else {
17174                 Roo.debug && Roo.log("build Order: no child modules");
17175             }
17176             // not sure if this is used any more..
17177             if (m.finalize) {
17178                 m.finalize.name = m.name + " (clean up) ";
17179                 mods.push(m.finalize);
17180             }
17181             
17182         }
17183         if (this.topModule && this.topModule.modules) { 
17184             this.topModule.modules.keySort('ASC',  cmp );
17185             this.topModule.modules.each(addMod);
17186         } 
17187         return mods;
17188     },
17189     
17190      /**
17191      * Build the registered modules.
17192      * @param {Object} parent element.
17193      * @param {Function} optional method to call after module has been added.
17194      * 
17195      */ 
17196    
17197     build : function(opts) 
17198     {
17199         
17200         if (typeof(opts) != 'undefined') {
17201             Roo.apply(this,opts);
17202         }
17203         
17204         this.preBuild();
17205         var mods = this.buildOrder();
17206       
17207         //this.allmods = mods;
17208         //Roo.debug && Roo.log(mods);
17209         //return;
17210         if (!mods.length) { // should not happen
17211             throw "NO modules!!!";
17212         }
17213         
17214         
17215         var msg = "Building Interface...";
17216         // flash it up as modal - so we store the mask!?
17217         if (!this.hideProgress && Roo.MessageBox) {
17218             Roo.MessageBox.show({ title: 'loading' });
17219             Roo.MessageBox.show({
17220                title: "Please wait...",
17221                msg: msg,
17222                width:450,
17223                progress:true,
17224                buttons : false,
17225                closable:false,
17226                modal: false
17227               
17228             });
17229         }
17230         var total = mods.length;
17231         
17232         var _this = this;
17233         var progressRun = function() {
17234             if (!mods.length) {
17235                 Roo.debug && Roo.log('hide?');
17236                 if (!this.hideProgress && Roo.MessageBox) {
17237                     Roo.MessageBox.hide();
17238                 }
17239                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
17240                 
17241                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
17242                 
17243                 // THE END...
17244                 return false;   
17245             }
17246             
17247             var m = mods.shift();
17248             
17249             
17250             Roo.debug && Roo.log(m);
17251             // not sure if this is supported any more.. - modules that are are just function
17252             if (typeof(m) == 'function') { 
17253                 m.call(this);
17254                 return progressRun.defer(10, _this);
17255             } 
17256             
17257             
17258             msg = "Building Interface " + (total  - mods.length) + 
17259                     " of " + total + 
17260                     (m.name ? (' - ' + m.name) : '');
17261                         Roo.debug && Roo.log(msg);
17262             if (!_this.hideProgress &&  Roo.MessageBox) { 
17263                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
17264             }
17265             
17266          
17267             // is the module disabled?
17268             var disabled = (typeof(m.disabled) == 'function') ?
17269                 m.disabled.call(m.module.disabled) : m.disabled;    
17270             
17271             
17272             if (disabled) {
17273                 return progressRun(); // we do not update the display!
17274             }
17275             
17276             // now build 
17277             
17278                         
17279                         
17280             m.render();
17281             // it's 10 on top level, and 1 on others??? why...
17282             return progressRun.defer(10, _this);
17283              
17284         }
17285         progressRun.defer(1, _this);
17286      
17287         
17288         
17289     },
17290     /**
17291      * Overlay a set of modified strings onto a component
17292      * This is dependant on our builder exporting the strings and 'named strings' elements.
17293      * 
17294      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
17295      * @param {Object} associative array of 'named' string and it's new value.
17296      * 
17297      */
17298         overlayStrings : function( component, strings )
17299     {
17300         if (typeof(component['_named_strings']) == 'undefined') {
17301             throw "ERROR: component does not have _named_strings";
17302         }
17303         for ( var k in strings ) {
17304             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
17305             if (md !== false) {
17306                 component['_strings'][md] = strings[k];
17307             } else {
17308                 Roo.log('could not find named string: ' + k + ' in');
17309                 Roo.log(component);
17310             }
17311             
17312         }
17313         
17314     },
17315     
17316         
17317         /**
17318          * Event Object.
17319          *
17320          *
17321          */
17322         event: false, 
17323     /**
17324          * wrapper for event.on - aliased later..  
17325          * Typically use to register a event handler for register:
17326          *
17327          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
17328          *
17329          */
17330     on : false
17331    
17332     
17333     
17334 });
17335
17336 Roo.XComponent.event = new Roo.util.Observable({
17337                 events : { 
17338                         /**
17339                          * @event register
17340                          * Fires when an Component is registered,
17341                          * set the disable property on the Component to stop registration.
17342                          * @param {Roo.XComponent} c the component being registerd.
17343                          * 
17344                          */
17345                         'register' : true,
17346             /**
17347                          * @event beforebuild
17348                          * Fires before each Component is built
17349                          * can be used to apply permissions.
17350                          * @param {Roo.XComponent} c the component being registerd.
17351                          * 
17352                          */
17353                         'beforebuild' : true,
17354                         /**
17355                          * @event buildcomplete
17356                          * Fires on the top level element when all elements have been built
17357                          * @param {Roo.XComponent} the top level component.
17358                          */
17359                         'buildcomplete' : true
17360                         
17361                 }
17362 });
17363
17364 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
17365  //
17366  /**
17367  * marked - a markdown parser
17368  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
17369  * https://github.com/chjj/marked
17370  */
17371
17372
17373 /**
17374  *
17375  * Roo.Markdown - is a very crude wrapper around marked..
17376  *
17377  * usage:
17378  * 
17379  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
17380  * 
17381  * Note: move the sample code to the bottom of this
17382  * file before uncommenting it.
17383  *
17384  */
17385
17386 Roo.Markdown = {};
17387 Roo.Markdown.toHtml = function(text) {
17388     
17389     var c = new Roo.Markdown.marked.setOptions({
17390             renderer: new Roo.Markdown.marked.Renderer(),
17391             gfm: true,
17392             tables: true,
17393             breaks: false,
17394             pedantic: false,
17395             sanitize: false,
17396             smartLists: true,
17397             smartypants: false
17398           });
17399     // A FEW HACKS!!?
17400     
17401     text = text.replace(/\\\n/g,' ');
17402     return Roo.Markdown.marked(text);
17403 };
17404 //
17405 // converter
17406 //
17407 // Wraps all "globals" so that the only thing
17408 // exposed is makeHtml().
17409 //
17410 (function() {
17411     
17412      /**
17413          * eval:var:escape
17414          * eval:var:unescape
17415          * eval:var:replace
17416          */
17417       
17418     /**
17419      * Helpers
17420      */
17421     
17422     var escape = function (html, encode) {
17423       return html
17424         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17425         .replace(/</g, '&lt;')
17426         .replace(/>/g, '&gt;')
17427         .replace(/"/g, '&quot;')
17428         .replace(/'/g, '&#39;');
17429     }
17430     
17431     var unescape = function (html) {
17432         // explicitly match decimal, hex, and named HTML entities 
17433       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17434         n = n.toLowerCase();
17435         if (n === 'colon') { return ':'; }
17436         if (n.charAt(0) === '#') {
17437           return n.charAt(1) === 'x'
17438             ? String.fromCharCode(parseInt(n.substring(2), 16))
17439             : String.fromCharCode(+n.substring(1));
17440         }
17441         return '';
17442       });
17443     }
17444     
17445     var replace = function (regex, opt) {
17446       regex = regex.source;
17447       opt = opt || '';
17448       return function self(name, val) {
17449         if (!name) { return new RegExp(regex, opt); }
17450         val = val.source || val;
17451         val = val.replace(/(^|[^\[])\^/g, '$1');
17452         regex = regex.replace(name, val);
17453         return self;
17454       };
17455     }
17456
17457
17458          /**
17459          * eval:var:noop
17460     */
17461     var noop = function () {}
17462     noop.exec = noop;
17463     
17464          /**
17465          * eval:var:merge
17466     */
17467     var merge = function (obj) {
17468       var i = 1
17469         , target
17470         , key;
17471     
17472       for (; i < arguments.length; i++) {
17473         target = arguments[i];
17474         for (key in target) {
17475           if (Object.prototype.hasOwnProperty.call(target, key)) {
17476             obj[key] = target[key];
17477           }
17478         }
17479       }
17480     
17481       return obj;
17482     }
17483     
17484     
17485     /**
17486      * Block-Level Grammar
17487      */
17488     
17489     
17490     
17491     
17492     var block = {
17493       newline: /^\n+/,
17494       code: /^( {4}[^\n]+\n*)+/,
17495       fences: noop,
17496       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17497       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
17498       nptable: noop,
17499       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
17500       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
17501       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
17502       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
17503       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
17504       table: noop,
17505       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
17506       text: /^[^\n]+/
17507     };
17508     
17509     block.bullet = /(?:[*+-]|\d+\.)/;
17510     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
17511     block.item = replace(block.item, 'gm')
17512       (/bull/g, block.bullet)
17513       ();
17514     
17515     block.list = replace(block.list)
17516       (/bull/g, block.bullet)
17517       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
17518       ('def', '\\n+(?=' + block.def.source + ')')
17519       ();
17520     
17521     block.blockquote = replace(block.blockquote)
17522       ('def', block.def)
17523       ();
17524     
17525     block._tag = '(?!(?:'
17526       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
17527       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
17528       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
17529     
17530     block.html = replace(block.html)
17531       ('comment', /<!--[\s\S]*?-->/)
17532       ('closed', /<(tag)[\s\S]+?<\/\1>/)
17533       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
17534       (/tag/g, block._tag)
17535       ();
17536     
17537     block.paragraph = replace(block.paragraph)
17538       ('hr', block.hr)
17539       ('heading', block.heading)
17540       ('lheading', block.lheading)
17541       ('blockquote', block.blockquote)
17542       ('tag', '<' + block._tag)
17543       ('def', block.def)
17544       ();
17545     
17546     /**
17547      * Normal Block Grammar
17548      */
17549     
17550     block.normal = merge({}, block);
17551     
17552     /**
17553      * GFM Block Grammar
17554      */
17555     
17556     block.gfm = merge({}, block.normal, {
17557       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
17558       paragraph: /^/,
17559       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
17560     });
17561     
17562     block.gfm.paragraph = replace(block.paragraph)
17563       ('(?!', '(?!'
17564         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
17565         + block.list.source.replace('\\1', '\\3') + '|')
17566       ();
17567     
17568     /**
17569      * GFM + Tables Block Grammar
17570      */
17571     
17572     block.tables = merge({}, block.gfm, {
17573       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
17574       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
17575     });
17576     
17577     /**
17578      * Block Lexer
17579      */
17580     
17581     var Lexer = function (options) {
17582       this.tokens = [];
17583       this.tokens.links = {};
17584       this.options = options || marked.defaults;
17585       this.rules = block.normal;
17586     
17587       if (this.options.gfm) {
17588         if (this.options.tables) {
17589           this.rules = block.tables;
17590         } else {
17591           this.rules = block.gfm;
17592         }
17593       }
17594     }
17595     
17596     /**
17597      * Expose Block Rules
17598      */
17599     
17600     Lexer.rules = block;
17601     
17602     /**
17603      * Static Lex Method
17604      */
17605     
17606     Lexer.lex = function(src, options) {
17607       var lexer = new Lexer(options);
17608       return lexer.lex(src);
17609     };
17610     
17611     /**
17612      * Preprocessing
17613      */
17614     
17615     Lexer.prototype.lex = function(src) {
17616       src = src
17617         .replace(/\r\n|\r/g, '\n')
17618         .replace(/\t/g, '    ')
17619         .replace(/\u00a0/g, ' ')
17620         .replace(/\u2424/g, '\n');
17621     
17622       return this.token(src, true);
17623     };
17624     
17625     /**
17626      * Lexing
17627      */
17628     
17629     Lexer.prototype.token = function(src, top, bq) {
17630       var src = src.replace(/^ +$/gm, '')
17631         , next
17632         , loose
17633         , cap
17634         , bull
17635         , b
17636         , item
17637         , space
17638         , i
17639         , l;
17640     
17641       while (src) {
17642         // newline
17643         if (cap = this.rules.newline.exec(src)) {
17644           src = src.substring(cap[0].length);
17645           if (cap[0].length > 1) {
17646             this.tokens.push({
17647               type: 'space'
17648             });
17649           }
17650         }
17651     
17652         // code
17653         if (cap = this.rules.code.exec(src)) {
17654           src = src.substring(cap[0].length);
17655           cap = cap[0].replace(/^ {4}/gm, '');
17656           this.tokens.push({
17657             type: 'code',
17658             text: !this.options.pedantic
17659               ? cap.replace(/\n+$/, '')
17660               : cap
17661           });
17662           continue;
17663         }
17664     
17665         // fences (gfm)
17666         if (cap = this.rules.fences.exec(src)) {
17667           src = src.substring(cap[0].length);
17668           this.tokens.push({
17669             type: 'code',
17670             lang: cap[2],
17671             text: cap[3] || ''
17672           });
17673           continue;
17674         }
17675     
17676         // heading
17677         if (cap = this.rules.heading.exec(src)) {
17678           src = src.substring(cap[0].length);
17679           this.tokens.push({
17680             type: 'heading',
17681             depth: cap[1].length,
17682             text: cap[2]
17683           });
17684           continue;
17685         }
17686     
17687         // table no leading pipe (gfm)
17688         if (top && (cap = this.rules.nptable.exec(src))) {
17689           src = src.substring(cap[0].length);
17690     
17691           item = {
17692             type: 'table',
17693             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17694             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17695             cells: cap[3].replace(/\n$/, '').split('\n')
17696           };
17697     
17698           for (i = 0; i < item.align.length; i++) {
17699             if (/^ *-+: *$/.test(item.align[i])) {
17700               item.align[i] = 'right';
17701             } else if (/^ *:-+: *$/.test(item.align[i])) {
17702               item.align[i] = 'center';
17703             } else if (/^ *:-+ *$/.test(item.align[i])) {
17704               item.align[i] = 'left';
17705             } else {
17706               item.align[i] = null;
17707             }
17708           }
17709     
17710           for (i = 0; i < item.cells.length; i++) {
17711             item.cells[i] = item.cells[i].split(/ *\| */);
17712           }
17713     
17714           this.tokens.push(item);
17715     
17716           continue;
17717         }
17718     
17719         // lheading
17720         if (cap = this.rules.lheading.exec(src)) {
17721           src = src.substring(cap[0].length);
17722           this.tokens.push({
17723             type: 'heading',
17724             depth: cap[2] === '=' ? 1 : 2,
17725             text: cap[1]
17726           });
17727           continue;
17728         }
17729     
17730         // hr
17731         if (cap = this.rules.hr.exec(src)) {
17732           src = src.substring(cap[0].length);
17733           this.tokens.push({
17734             type: 'hr'
17735           });
17736           continue;
17737         }
17738     
17739         // blockquote
17740         if (cap = this.rules.blockquote.exec(src)) {
17741           src = src.substring(cap[0].length);
17742     
17743           this.tokens.push({
17744             type: 'blockquote_start'
17745           });
17746     
17747           cap = cap[0].replace(/^ *> ?/gm, '');
17748     
17749           // Pass `top` to keep the current
17750           // "toplevel" state. This is exactly
17751           // how markdown.pl works.
17752           this.token(cap, top, true);
17753     
17754           this.tokens.push({
17755             type: 'blockquote_end'
17756           });
17757     
17758           continue;
17759         }
17760     
17761         // list
17762         if (cap = this.rules.list.exec(src)) {
17763           src = src.substring(cap[0].length);
17764           bull = cap[2];
17765     
17766           this.tokens.push({
17767             type: 'list_start',
17768             ordered: bull.length > 1
17769           });
17770     
17771           // Get each top-level item.
17772           cap = cap[0].match(this.rules.item);
17773     
17774           next = false;
17775           l = cap.length;
17776           i = 0;
17777     
17778           for (; i < l; i++) {
17779             item = cap[i];
17780     
17781             // Remove the list item's bullet
17782             // so it is seen as the next token.
17783             space = item.length;
17784             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17785     
17786             // Outdent whatever the
17787             // list item contains. Hacky.
17788             if (~item.indexOf('\n ')) {
17789               space -= item.length;
17790               item = !this.options.pedantic
17791                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17792                 : item.replace(/^ {1,4}/gm, '');
17793             }
17794     
17795             // Determine whether the next list item belongs here.
17796             // Backpedal if it does not belong in this list.
17797             if (this.options.smartLists && i !== l - 1) {
17798               b = block.bullet.exec(cap[i + 1])[0];
17799               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17800                 src = cap.slice(i + 1).join('\n') + src;
17801                 i = l - 1;
17802               }
17803             }
17804     
17805             // Determine whether item is loose or not.
17806             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17807             // for discount behavior.
17808             loose = next || /\n\n(?!\s*$)/.test(item);
17809             if (i !== l - 1) {
17810               next = item.charAt(item.length - 1) === '\n';
17811               if (!loose) { loose = next; }
17812             }
17813     
17814             this.tokens.push({
17815               type: loose
17816                 ? 'loose_item_start'
17817                 : 'list_item_start'
17818             });
17819     
17820             // Recurse.
17821             this.token(item, false, bq);
17822     
17823             this.tokens.push({
17824               type: 'list_item_end'
17825             });
17826           }
17827     
17828           this.tokens.push({
17829             type: 'list_end'
17830           });
17831     
17832           continue;
17833         }
17834     
17835         // html
17836         if (cap = this.rules.html.exec(src)) {
17837           src = src.substring(cap[0].length);
17838           this.tokens.push({
17839             type: this.options.sanitize
17840               ? 'paragraph'
17841               : 'html',
17842             pre: !this.options.sanitizer
17843               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17844             text: cap[0]
17845           });
17846           continue;
17847         }
17848     
17849         // def
17850         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17851           src = src.substring(cap[0].length);
17852           this.tokens.links[cap[1].toLowerCase()] = {
17853             href: cap[2],
17854             title: cap[3]
17855           };
17856           continue;
17857         }
17858     
17859         // table (gfm)
17860         if (top && (cap = this.rules.table.exec(src))) {
17861           src = src.substring(cap[0].length);
17862     
17863           item = {
17864             type: 'table',
17865             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17866             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17867             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17868           };
17869     
17870           for (i = 0; i < item.align.length; i++) {
17871             if (/^ *-+: *$/.test(item.align[i])) {
17872               item.align[i] = 'right';
17873             } else if (/^ *:-+: *$/.test(item.align[i])) {
17874               item.align[i] = 'center';
17875             } else if (/^ *:-+ *$/.test(item.align[i])) {
17876               item.align[i] = 'left';
17877             } else {
17878               item.align[i] = null;
17879             }
17880           }
17881     
17882           for (i = 0; i < item.cells.length; i++) {
17883             item.cells[i] = item.cells[i]
17884               .replace(/^ *\| *| *\| *$/g, '')
17885               .split(/ *\| */);
17886           }
17887     
17888           this.tokens.push(item);
17889     
17890           continue;
17891         }
17892     
17893         // top-level paragraph
17894         if (top && (cap = this.rules.paragraph.exec(src))) {
17895           src = src.substring(cap[0].length);
17896           this.tokens.push({
17897             type: 'paragraph',
17898             text: cap[1].charAt(cap[1].length - 1) === '\n'
17899               ? cap[1].slice(0, -1)
17900               : cap[1]
17901           });
17902           continue;
17903         }
17904     
17905         // text
17906         if (cap = this.rules.text.exec(src)) {
17907           // Top-level should never reach here.
17908           src = src.substring(cap[0].length);
17909           this.tokens.push({
17910             type: 'text',
17911             text: cap[0]
17912           });
17913           continue;
17914         }
17915     
17916         if (src) {
17917           throw new
17918             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17919         }
17920       }
17921     
17922       return this.tokens;
17923     };
17924     
17925     /**
17926      * Inline-Level Grammar
17927      */
17928     
17929     var inline = {
17930       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17931       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17932       url: noop,
17933       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17934       link: /^!?\[(inside)\]\(href\)/,
17935       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17936       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17937       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17938       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17939       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17940       br: /^ {2,}\n(?!\s*$)/,
17941       del: noop,
17942       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17943     };
17944     
17945     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17946     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17947     
17948     inline.link = replace(inline.link)
17949       ('inside', inline._inside)
17950       ('href', inline._href)
17951       ();
17952     
17953     inline.reflink = replace(inline.reflink)
17954       ('inside', inline._inside)
17955       ();
17956     
17957     /**
17958      * Normal Inline Grammar
17959      */
17960     
17961     inline.normal = merge({}, inline);
17962     
17963     /**
17964      * Pedantic Inline Grammar
17965      */
17966     
17967     inline.pedantic = merge({}, inline.normal, {
17968       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17969       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17970     });
17971     
17972     /**
17973      * GFM Inline Grammar
17974      */
17975     
17976     inline.gfm = merge({}, inline.normal, {
17977       escape: replace(inline.escape)('])', '~|])')(),
17978       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17979       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17980       text: replace(inline.text)
17981         (']|', '~]|')
17982         ('|', '|https?://|')
17983         ()
17984     });
17985     
17986     /**
17987      * GFM + Line Breaks Inline Grammar
17988      */
17989     
17990     inline.breaks = merge({}, inline.gfm, {
17991       br: replace(inline.br)('{2,}', '*')(),
17992       text: replace(inline.gfm.text)('{2,}', '*')()
17993     });
17994     
17995     /**
17996      * Inline Lexer & Compiler
17997      */
17998     
17999     var InlineLexer  = function (links, options) {
18000       this.options = options || marked.defaults;
18001       this.links = links;
18002       this.rules = inline.normal;
18003       this.renderer = this.options.renderer || new Renderer;
18004       this.renderer.options = this.options;
18005     
18006       if (!this.links) {
18007         throw new
18008           Error('Tokens array requires a `links` property.');
18009       }
18010     
18011       if (this.options.gfm) {
18012         if (this.options.breaks) {
18013           this.rules = inline.breaks;
18014         } else {
18015           this.rules = inline.gfm;
18016         }
18017       } else if (this.options.pedantic) {
18018         this.rules = inline.pedantic;
18019       }
18020     }
18021     
18022     /**
18023      * Expose Inline Rules
18024      */
18025     
18026     InlineLexer.rules = inline;
18027     
18028     /**
18029      * Static Lexing/Compiling Method
18030      */
18031     
18032     InlineLexer.output = function(src, links, options) {
18033       var inline = new InlineLexer(links, options);
18034       return inline.output(src);
18035     };
18036     
18037     /**
18038      * Lexing/Compiling
18039      */
18040     
18041     InlineLexer.prototype.output = function(src) {
18042       var out = ''
18043         , link
18044         , text
18045         , href
18046         , cap;
18047     
18048       while (src) {
18049         // escape
18050         if (cap = this.rules.escape.exec(src)) {
18051           src = src.substring(cap[0].length);
18052           out += cap[1];
18053           continue;
18054         }
18055     
18056         // autolink
18057         if (cap = this.rules.autolink.exec(src)) {
18058           src = src.substring(cap[0].length);
18059           if (cap[2] === '@') {
18060             text = cap[1].charAt(6) === ':'
18061               ? this.mangle(cap[1].substring(7))
18062               : this.mangle(cap[1]);
18063             href = this.mangle('mailto:') + text;
18064           } else {
18065             text = escape(cap[1]);
18066             href = text;
18067           }
18068           out += this.renderer.link(href, null, text);
18069           continue;
18070         }
18071     
18072         // url (gfm)
18073         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18074           src = src.substring(cap[0].length);
18075           text = escape(cap[1]);
18076           href = text;
18077           out += this.renderer.link(href, null, text);
18078           continue;
18079         }
18080     
18081         // tag
18082         if (cap = this.rules.tag.exec(src)) {
18083           if (!this.inLink && /^<a /i.test(cap[0])) {
18084             this.inLink = true;
18085           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18086             this.inLink = false;
18087           }
18088           src = src.substring(cap[0].length);
18089           out += this.options.sanitize
18090             ? this.options.sanitizer
18091               ? this.options.sanitizer(cap[0])
18092               : escape(cap[0])
18093             : cap[0];
18094           continue;
18095         }
18096     
18097         // link
18098         if (cap = this.rules.link.exec(src)) {
18099           src = src.substring(cap[0].length);
18100           this.inLink = true;
18101           out += this.outputLink(cap, {
18102             href: cap[2],
18103             title: cap[3]
18104           });
18105           this.inLink = false;
18106           continue;
18107         }
18108     
18109         // reflink, nolink
18110         if ((cap = this.rules.reflink.exec(src))
18111             || (cap = this.rules.nolink.exec(src))) {
18112           src = src.substring(cap[0].length);
18113           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18114           link = this.links[link.toLowerCase()];
18115           if (!link || !link.href) {
18116             out += cap[0].charAt(0);
18117             src = cap[0].substring(1) + src;
18118             continue;
18119           }
18120           this.inLink = true;
18121           out += this.outputLink(cap, link);
18122           this.inLink = false;
18123           continue;
18124         }
18125     
18126         // strong
18127         if (cap = this.rules.strong.exec(src)) {
18128           src = src.substring(cap[0].length);
18129           out += this.renderer.strong(this.output(cap[2] || cap[1]));
18130           continue;
18131         }
18132     
18133         // em
18134         if (cap = this.rules.em.exec(src)) {
18135           src = src.substring(cap[0].length);
18136           out += this.renderer.em(this.output(cap[2] || cap[1]));
18137           continue;
18138         }
18139     
18140         // code
18141         if (cap = this.rules.code.exec(src)) {
18142           src = src.substring(cap[0].length);
18143           out += this.renderer.codespan(escape(cap[2], true));
18144           continue;
18145         }
18146     
18147         // br
18148         if (cap = this.rules.br.exec(src)) {
18149           src = src.substring(cap[0].length);
18150           out += this.renderer.br();
18151           continue;
18152         }
18153     
18154         // del (gfm)
18155         if (cap = this.rules.del.exec(src)) {
18156           src = src.substring(cap[0].length);
18157           out += this.renderer.del(this.output(cap[1]));
18158           continue;
18159         }
18160     
18161         // text
18162         if (cap = this.rules.text.exec(src)) {
18163           src = src.substring(cap[0].length);
18164           out += this.renderer.text(escape(this.smartypants(cap[0])));
18165           continue;
18166         }
18167     
18168         if (src) {
18169           throw new
18170             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18171         }
18172       }
18173     
18174       return out;
18175     };
18176     
18177     /**
18178      * Compile Link
18179      */
18180     
18181     InlineLexer.prototype.outputLink = function(cap, link) {
18182       var href = escape(link.href)
18183         , title = link.title ? escape(link.title) : null;
18184     
18185       return cap[0].charAt(0) !== '!'
18186         ? this.renderer.link(href, title, this.output(cap[1]))
18187         : this.renderer.image(href, title, escape(cap[1]));
18188     };
18189     
18190     /**
18191      * Smartypants Transformations
18192      */
18193     
18194     InlineLexer.prototype.smartypants = function(text) {
18195       if (!this.options.smartypants)  { return text; }
18196       return text
18197         // em-dashes
18198         .replace(/---/g, '\u2014')
18199         // en-dashes
18200         .replace(/--/g, '\u2013')
18201         // opening singles
18202         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
18203         // closing singles & apostrophes
18204         .replace(/'/g, '\u2019')
18205         // opening doubles
18206         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
18207         // closing doubles
18208         .replace(/"/g, '\u201d')
18209         // ellipses
18210         .replace(/\.{3}/g, '\u2026');
18211     };
18212     
18213     /**
18214      * Mangle Links
18215      */
18216     
18217     InlineLexer.prototype.mangle = function(text) {
18218       if (!this.options.mangle) { return text; }
18219       var out = ''
18220         , l = text.length
18221         , i = 0
18222         , ch;
18223     
18224       for (; i < l; i++) {
18225         ch = text.charCodeAt(i);
18226         if (Math.random() > 0.5) {
18227           ch = 'x' + ch.toString(16);
18228         }
18229         out += '&#' + ch + ';';
18230       }
18231     
18232       return out;
18233     };
18234     
18235     /**
18236      * Renderer
18237      */
18238     
18239      /**
18240          * eval:var:Renderer
18241     */
18242     
18243     var Renderer   = function (options) {
18244       this.options = options || {};
18245     }
18246     
18247     Renderer.prototype.code = function(code, lang, escaped) {
18248       if (this.options.highlight) {
18249         var out = this.options.highlight(code, lang);
18250         if (out != null && out !== code) {
18251           escaped = true;
18252           code = out;
18253         }
18254       } else {
18255             // hack!!! - it's already escapeD?
18256             escaped = true;
18257       }
18258     
18259       if (!lang) {
18260         return '<pre><code>'
18261           + (escaped ? code : escape(code, true))
18262           + '\n</code></pre>';
18263       }
18264     
18265       return '<pre><code class="'
18266         + this.options.langPrefix
18267         + escape(lang, true)
18268         + '">'
18269         + (escaped ? code : escape(code, true))
18270         + '\n</code></pre>\n';
18271     };
18272     
18273     Renderer.prototype.blockquote = function(quote) {
18274       return '<blockquote>\n' + quote + '</blockquote>\n';
18275     };
18276     
18277     Renderer.prototype.html = function(html) {
18278       return html;
18279     };
18280     
18281     Renderer.prototype.heading = function(text, level, raw) {
18282       return '<h'
18283         + level
18284         + ' id="'
18285         + this.options.headerPrefix
18286         + raw.toLowerCase().replace(/[^\w]+/g, '-')
18287         + '">'
18288         + text
18289         + '</h'
18290         + level
18291         + '>\n';
18292     };
18293     
18294     Renderer.prototype.hr = function() {
18295       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
18296     };
18297     
18298     Renderer.prototype.list = function(body, ordered) {
18299       var type = ordered ? 'ol' : 'ul';
18300       return '<' + type + '>\n' + body + '</' + type + '>\n';
18301     };
18302     
18303     Renderer.prototype.listitem = function(text) {
18304       return '<li>' + text + '</li>\n';
18305     };
18306     
18307     Renderer.prototype.paragraph = function(text) {
18308       return '<p>' + text + '</p>\n';
18309     };
18310     
18311     Renderer.prototype.table = function(header, body) {
18312       return '<table class="table table-striped">\n'
18313         + '<thead>\n'
18314         + header
18315         + '</thead>\n'
18316         + '<tbody>\n'
18317         + body
18318         + '</tbody>\n'
18319         + '</table>\n';
18320     };
18321     
18322     Renderer.prototype.tablerow = function(content) {
18323       return '<tr>\n' + content + '</tr>\n';
18324     };
18325     
18326     Renderer.prototype.tablecell = function(content, flags) {
18327       var type = flags.header ? 'th' : 'td';
18328       var tag = flags.align
18329         ? '<' + type + ' style="text-align:' + flags.align + '">'
18330         : '<' + type + '>';
18331       return tag + content + '</' + type + '>\n';
18332     };
18333     
18334     // span level renderer
18335     Renderer.prototype.strong = function(text) {
18336       return '<strong>' + text + '</strong>';
18337     };
18338     
18339     Renderer.prototype.em = function(text) {
18340       return '<em>' + text + '</em>';
18341     };
18342     
18343     Renderer.prototype.codespan = function(text) {
18344       return '<code>' + text + '</code>';
18345     };
18346     
18347     Renderer.prototype.br = function() {
18348       return this.options.xhtml ? '<br/>' : '<br>';
18349     };
18350     
18351     Renderer.prototype.del = function(text) {
18352       return '<del>' + text + '</del>';
18353     };
18354     
18355     Renderer.prototype.link = function(href, title, text) {
18356       if (this.options.sanitize) {
18357         try {
18358           var prot = decodeURIComponent(unescape(href))
18359             .replace(/[^\w:]/g, '')
18360             .toLowerCase();
18361         } catch (e) {
18362           return '';
18363         }
18364         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
18365           return '';
18366         }
18367       }
18368       var out = '<a href="' + href + '"';
18369       if (title) {
18370         out += ' title="' + title + '"';
18371       }
18372       out += '>' + text + '</a>';
18373       return out;
18374     };
18375     
18376     Renderer.prototype.image = function(href, title, text) {
18377       var out = '<img src="' + href + '" alt="' + text + '"';
18378       if (title) {
18379         out += ' title="' + title + '"';
18380       }
18381       out += this.options.xhtml ? '/>' : '>';
18382       return out;
18383     };
18384     
18385     Renderer.prototype.text = function(text) {
18386       return text;
18387     };
18388     
18389     /**
18390      * Parsing & Compiling
18391      */
18392          /**
18393          * eval:var:Parser
18394     */
18395     
18396     var Parser= function (options) {
18397       this.tokens = [];
18398       this.token = null;
18399       this.options = options || marked.defaults;
18400       this.options.renderer = this.options.renderer || new Renderer;
18401       this.renderer = this.options.renderer;
18402       this.renderer.options = this.options;
18403     }
18404     
18405     /**
18406      * Static Parse Method
18407      */
18408     
18409     Parser.parse = function(src, options, renderer) {
18410       var parser = new Parser(options, renderer);
18411       return parser.parse(src);
18412     };
18413     
18414     /**
18415      * Parse Loop
18416      */
18417     
18418     Parser.prototype.parse = function(src) {
18419       this.inline = new InlineLexer(src.links, this.options, this.renderer);
18420       this.tokens = src.reverse();
18421     
18422       var out = '';
18423       while (this.next()) {
18424         out += this.tok();
18425       }
18426     
18427       return out;
18428     };
18429     
18430     /**
18431      * Next Token
18432      */
18433     
18434     Parser.prototype.next = function() {
18435       return this.token = this.tokens.pop();
18436     };
18437     
18438     /**
18439      * Preview Next Token
18440      */
18441     
18442     Parser.prototype.peek = function() {
18443       return this.tokens[this.tokens.length - 1] || 0;
18444     };
18445     
18446     /**
18447      * Parse Text Tokens
18448      */
18449     
18450     Parser.prototype.parseText = function() {
18451       var body = this.token.text;
18452     
18453       while (this.peek().type === 'text') {
18454         body += '\n' + this.next().text;
18455       }
18456     
18457       return this.inline.output(body);
18458     };
18459     
18460     /**
18461      * Parse Current Token
18462      */
18463     
18464     Parser.prototype.tok = function() {
18465       switch (this.token.type) {
18466         case 'space': {
18467           return '';
18468         }
18469         case 'hr': {
18470           return this.renderer.hr();
18471         }
18472         case 'heading': {
18473           return this.renderer.heading(
18474             this.inline.output(this.token.text),
18475             this.token.depth,
18476             this.token.text);
18477         }
18478         case 'code': {
18479           return this.renderer.code(this.token.text,
18480             this.token.lang,
18481             this.token.escaped);
18482         }
18483         case 'table': {
18484           var header = ''
18485             , body = ''
18486             , i
18487             , row
18488             , cell
18489             , flags
18490             , j;
18491     
18492           // header
18493           cell = '';
18494           for (i = 0; i < this.token.header.length; i++) {
18495             flags = { header: true, align: this.token.align[i] };
18496             cell += this.renderer.tablecell(
18497               this.inline.output(this.token.header[i]),
18498               { header: true, align: this.token.align[i] }
18499             );
18500           }
18501           header += this.renderer.tablerow(cell);
18502     
18503           for (i = 0; i < this.token.cells.length; i++) {
18504             row = this.token.cells[i];
18505     
18506             cell = '';
18507             for (j = 0; j < row.length; j++) {
18508               cell += this.renderer.tablecell(
18509                 this.inline.output(row[j]),
18510                 { header: false, align: this.token.align[j] }
18511               );
18512             }
18513     
18514             body += this.renderer.tablerow(cell);
18515           }
18516           return this.renderer.table(header, body);
18517         }
18518         case 'blockquote_start': {
18519           var body = '';
18520     
18521           while (this.next().type !== 'blockquote_end') {
18522             body += this.tok();
18523           }
18524     
18525           return this.renderer.blockquote(body);
18526         }
18527         case 'list_start': {
18528           var body = ''
18529             , ordered = this.token.ordered;
18530     
18531           while (this.next().type !== 'list_end') {
18532             body += this.tok();
18533           }
18534     
18535           return this.renderer.list(body, ordered);
18536         }
18537         case 'list_item_start': {
18538           var body = '';
18539     
18540           while (this.next().type !== 'list_item_end') {
18541             body += this.token.type === 'text'
18542               ? this.parseText()
18543               : this.tok();
18544           }
18545     
18546           return this.renderer.listitem(body);
18547         }
18548         case 'loose_item_start': {
18549           var body = '';
18550     
18551           while (this.next().type !== 'list_item_end') {
18552             body += this.tok();
18553           }
18554     
18555           return this.renderer.listitem(body);
18556         }
18557         case 'html': {
18558           var html = !this.token.pre && !this.options.pedantic
18559             ? this.inline.output(this.token.text)
18560             : this.token.text;
18561           return this.renderer.html(html);
18562         }
18563         case 'paragraph': {
18564           return this.renderer.paragraph(this.inline.output(this.token.text));
18565         }
18566         case 'text': {
18567           return this.renderer.paragraph(this.parseText());
18568         }
18569       }
18570     };
18571   
18572     
18573     /**
18574      * Marked
18575      */
18576          /**
18577          * eval:var:marked
18578     */
18579     var marked = function (src, opt, callback) {
18580       if (callback || typeof opt === 'function') {
18581         if (!callback) {
18582           callback = opt;
18583           opt = null;
18584         }
18585     
18586         opt = merge({}, marked.defaults, opt || {});
18587     
18588         var highlight = opt.highlight
18589           , tokens
18590           , pending
18591           , i = 0;
18592     
18593         try {
18594           tokens = Lexer.lex(src, opt)
18595         } catch (e) {
18596           return callback(e);
18597         }
18598     
18599         pending = tokens.length;
18600          /**
18601          * eval:var:done
18602     */
18603         var done = function(err) {
18604           if (err) {
18605             opt.highlight = highlight;
18606             return callback(err);
18607           }
18608     
18609           var out;
18610     
18611           try {
18612             out = Parser.parse(tokens, opt);
18613           } catch (e) {
18614             err = e;
18615           }
18616     
18617           opt.highlight = highlight;
18618     
18619           return err
18620             ? callback(err)
18621             : callback(null, out);
18622         };
18623     
18624         if (!highlight || highlight.length < 3) {
18625           return done();
18626         }
18627     
18628         delete opt.highlight;
18629     
18630         if (!pending) { return done(); }
18631     
18632         for (; i < tokens.length; i++) {
18633           (function(token) {
18634             if (token.type !== 'code') {
18635               return --pending || done();
18636             }
18637             return highlight(token.text, token.lang, function(err, code) {
18638               if (err) { return done(err); }
18639               if (code == null || code === token.text) {
18640                 return --pending || done();
18641               }
18642               token.text = code;
18643               token.escaped = true;
18644               --pending || done();
18645             });
18646           })(tokens[i]);
18647         }
18648     
18649         return;
18650       }
18651       try {
18652         if (opt) { opt = merge({}, marked.defaults, opt); }
18653         return Parser.parse(Lexer.lex(src, opt), opt);
18654       } catch (e) {
18655         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18656         if ((opt || marked.defaults).silent) {
18657           return '<p>An error occured:</p><pre>'
18658             + escape(e.message + '', true)
18659             + '</pre>';
18660         }
18661         throw e;
18662       }
18663     }
18664     
18665     /**
18666      * Options
18667      */
18668     
18669     marked.options =
18670     marked.setOptions = function(opt) {
18671       merge(marked.defaults, opt);
18672       return marked;
18673     };
18674     
18675     marked.defaults = {
18676       gfm: true,
18677       tables: true,
18678       breaks: false,
18679       pedantic: false,
18680       sanitize: false,
18681       sanitizer: null,
18682       mangle: true,
18683       smartLists: false,
18684       silent: false,
18685       highlight: null,
18686       langPrefix: 'lang-',
18687       smartypants: false,
18688       headerPrefix: '',
18689       renderer: new Renderer,
18690       xhtml: false
18691     };
18692     
18693     /**
18694      * Expose
18695      */
18696     
18697     marked.Parser = Parser;
18698     marked.parser = Parser.parse;
18699     
18700     marked.Renderer = Renderer;
18701     
18702     marked.Lexer = Lexer;
18703     marked.lexer = Lexer.lex;
18704     
18705     marked.InlineLexer = InlineLexer;
18706     marked.inlineLexer = InlineLexer.output;
18707     
18708     marked.parse = marked;
18709     
18710     Roo.Markdown.marked = marked;
18711
18712 })();/*
18713  * Based on:
18714  * Ext JS Library 1.1.1
18715  * Copyright(c) 2006-2007, Ext JS, LLC.
18716  *
18717  * Originally Released Under LGPL - original licence link has changed is not relivant.
18718  *
18719  * Fork - LGPL
18720  * <script type="text/javascript">
18721  */
18722
18723
18724
18725 /*
18726  * These classes are derivatives of the similarly named classes in the YUI Library.
18727  * The original license:
18728  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18729  * Code licensed under the BSD License:
18730  * http://developer.yahoo.net/yui/license.txt
18731  */
18732
18733 (function() {
18734
18735 var Event=Roo.EventManager;
18736 var Dom=Roo.lib.Dom;
18737
18738 /**
18739  * @class Roo.dd.DragDrop
18740  * @extends Roo.util.Observable
18741  * Defines the interface and base operation of items that that can be
18742  * dragged or can be drop targets.  It was designed to be extended, overriding
18743  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18744  * Up to three html elements can be associated with a DragDrop instance:
18745  * <ul>
18746  * <li>linked element: the element that is passed into the constructor.
18747  * This is the element which defines the boundaries for interaction with
18748  * other DragDrop objects.</li>
18749  * <li>handle element(s): The drag operation only occurs if the element that
18750  * was clicked matches a handle element.  By default this is the linked
18751  * element, but there are times that you will want only a portion of the
18752  * linked element to initiate the drag operation, and the setHandleElId()
18753  * method provides a way to define this.</li>
18754  * <li>drag element: this represents the element that would be moved along
18755  * with the cursor during a drag operation.  By default, this is the linked
18756  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18757  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18758  * </li>
18759  * </ul>
18760  * This class should not be instantiated until the onload event to ensure that
18761  * the associated elements are available.
18762  * The following would define a DragDrop obj that would interact with any
18763  * other DragDrop obj in the "group1" group:
18764  * <pre>
18765  *  dd = new Roo.dd.DragDrop("div1", "group1");
18766  * </pre>
18767  * Since none of the event handlers have been implemented, nothing would
18768  * actually happen if you were to run the code above.  Normally you would
18769  * override this class or one of the default implementations, but you can
18770  * also override the methods you want on an instance of the class...
18771  * <pre>
18772  *  dd.onDragDrop = function(e, id) {
18773  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18774  *  }
18775  * </pre>
18776  * @constructor
18777  * @param {String} id of the element that is linked to this instance
18778  * @param {String} sGroup the group of related DragDrop objects
18779  * @param {object} config an object containing configurable attributes
18780  *                Valid properties for DragDrop:
18781  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18782  */
18783 Roo.dd.DragDrop = function(id, sGroup, config) {
18784     if (id) {
18785         this.init(id, sGroup, config);
18786     }
18787     
18788 };
18789
18790 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18791
18792     /**
18793      * The id of the element associated with this object.  This is what we
18794      * refer to as the "linked element" because the size and position of
18795      * this element is used to determine when the drag and drop objects have
18796      * interacted.
18797      * @property id
18798      * @type String
18799      */
18800     id: null,
18801
18802     /**
18803      * Configuration attributes passed into the constructor
18804      * @property config
18805      * @type object
18806      */
18807     config: null,
18808
18809     /**
18810      * The id of the element that will be dragged.  By default this is same
18811      * as the linked element , but could be changed to another element. Ex:
18812      * Roo.dd.DDProxy
18813      * @property dragElId
18814      * @type String
18815      * @private
18816      */
18817     dragElId: null,
18818
18819     /**
18820      * the id of the element that initiates the drag operation.  By default
18821      * this is the linked element, but could be changed to be a child of this
18822      * element.  This lets us do things like only starting the drag when the
18823      * header element within the linked html element is clicked.
18824      * @property handleElId
18825      * @type String
18826      * @private
18827      */
18828     handleElId: null,
18829
18830     /**
18831      * An associative array of HTML tags that will be ignored if clicked.
18832      * @property invalidHandleTypes
18833      * @type {string: string}
18834      */
18835     invalidHandleTypes: null,
18836
18837     /**
18838      * An associative array of ids for elements that will be ignored if clicked
18839      * @property invalidHandleIds
18840      * @type {string: string}
18841      */
18842     invalidHandleIds: null,
18843
18844     /**
18845      * An indexted array of css class names for elements that will be ignored
18846      * if clicked.
18847      * @property invalidHandleClasses
18848      * @type string[]
18849      */
18850     invalidHandleClasses: null,
18851
18852     /**
18853      * The linked element's absolute X position at the time the drag was
18854      * started
18855      * @property startPageX
18856      * @type int
18857      * @private
18858      */
18859     startPageX: 0,
18860
18861     /**
18862      * The linked element's absolute X position at the time the drag was
18863      * started
18864      * @property startPageY
18865      * @type int
18866      * @private
18867      */
18868     startPageY: 0,
18869
18870     /**
18871      * The group defines a logical collection of DragDrop objects that are
18872      * related.  Instances only get events when interacting with other
18873      * DragDrop object in the same group.  This lets us define multiple
18874      * groups using a single DragDrop subclass if we want.
18875      * @property groups
18876      * @type {string: string}
18877      */
18878     groups: null,
18879
18880     /**
18881      * Individual drag/drop instances can be locked.  This will prevent
18882      * onmousedown start drag.
18883      * @property locked
18884      * @type boolean
18885      * @private
18886      */
18887     locked: false,
18888
18889     /**
18890      * Lock this instance
18891      * @method lock
18892      */
18893     lock: function() { this.locked = true; },
18894
18895     /**
18896      * Unlock this instace
18897      * @method unlock
18898      */
18899     unlock: function() { this.locked = false; },
18900
18901     /**
18902      * By default, all insances can be a drop target.  This can be disabled by
18903      * setting isTarget to false.
18904      * @method isTarget
18905      * @type boolean
18906      */
18907     isTarget: true,
18908
18909     /**
18910      * The padding configured for this drag and drop object for calculating
18911      * the drop zone intersection with this object.
18912      * @method padding
18913      * @type int[]
18914      */
18915     padding: null,
18916
18917     /**
18918      * Cached reference to the linked element
18919      * @property _domRef
18920      * @private
18921      */
18922     _domRef: null,
18923
18924     /**
18925      * Internal typeof flag
18926      * @property __ygDragDrop
18927      * @private
18928      */
18929     __ygDragDrop: true,
18930
18931     /**
18932      * Set to true when horizontal contraints are applied
18933      * @property constrainX
18934      * @type boolean
18935      * @private
18936      */
18937     constrainX: false,
18938
18939     /**
18940      * Set to true when vertical contraints are applied
18941      * @property constrainY
18942      * @type boolean
18943      * @private
18944      */
18945     constrainY: false,
18946
18947     /**
18948      * The left constraint
18949      * @property minX
18950      * @type int
18951      * @private
18952      */
18953     minX: 0,
18954
18955     /**
18956      * The right constraint
18957      * @property maxX
18958      * @type int
18959      * @private
18960      */
18961     maxX: 0,
18962
18963     /**
18964      * The up constraint
18965      * @property minY
18966      * @type int
18967      * @type int
18968      * @private
18969      */
18970     minY: 0,
18971
18972     /**
18973      * The down constraint
18974      * @property maxY
18975      * @type int
18976      * @private
18977      */
18978     maxY: 0,
18979
18980     /**
18981      * Maintain offsets when we resetconstraints.  Set to true when you want
18982      * the position of the element relative to its parent to stay the same
18983      * when the page changes
18984      *
18985      * @property maintainOffset
18986      * @type boolean
18987      */
18988     maintainOffset: false,
18989
18990     /**
18991      * Array of pixel locations the element will snap to if we specified a
18992      * horizontal graduation/interval.  This array is generated automatically
18993      * when you define a tick interval.
18994      * @property xTicks
18995      * @type int[]
18996      */
18997     xTicks: null,
18998
18999     /**
19000      * Array of pixel locations the element will snap to if we specified a
19001      * vertical graduation/interval.  This array is generated automatically
19002      * when you define a tick interval.
19003      * @property yTicks
19004      * @type int[]
19005      */
19006     yTicks: null,
19007
19008     /**
19009      * By default the drag and drop instance will only respond to the primary
19010      * button click (left button for a right-handed mouse).  Set to true to
19011      * allow drag and drop to start with any mouse click that is propogated
19012      * by the browser
19013      * @property primaryButtonOnly
19014      * @type boolean
19015      */
19016     primaryButtonOnly: true,
19017
19018     /**
19019      * The availabe property is false until the linked dom element is accessible.
19020      * @property available
19021      * @type boolean
19022      */
19023     available: false,
19024
19025     /**
19026      * By default, drags can only be initiated if the mousedown occurs in the
19027      * region the linked element is.  This is done in part to work around a
19028      * bug in some browsers that mis-report the mousedown if the previous
19029      * mouseup happened outside of the window.  This property is set to true
19030      * if outer handles are defined.
19031      *
19032      * @property hasOuterHandles
19033      * @type boolean
19034      * @default false
19035      */
19036     hasOuterHandles: false,
19037
19038     /**
19039      * Code that executes immediately before the startDrag event
19040      * @method b4StartDrag
19041      * @private
19042      */
19043     b4StartDrag: function(x, y) { },
19044
19045     /**
19046      * Abstract method called after a drag/drop object is clicked
19047      * and the drag or mousedown time thresholds have beeen met.
19048      * @method startDrag
19049      * @param {int} X click location
19050      * @param {int} Y click location
19051      */
19052     startDrag: function(x, y) { /* override this */ },
19053
19054     /**
19055      * Code that executes immediately before the onDrag event
19056      * @method b4Drag
19057      * @private
19058      */
19059     b4Drag: function(e) { },
19060
19061     /**
19062      * Abstract method called during the onMouseMove event while dragging an
19063      * object.
19064      * @method onDrag
19065      * @param {Event} e the mousemove event
19066      */
19067     onDrag: function(e) { /* override this */ },
19068
19069     /**
19070      * Abstract method called when this element fist begins hovering over
19071      * another DragDrop obj
19072      * @method onDragEnter
19073      * @param {Event} e the mousemove event
19074      * @param {String|DragDrop[]} id In POINT mode, the element
19075      * id this is hovering over.  In INTERSECT mode, an array of one or more
19076      * dragdrop items being hovered over.
19077      */
19078     onDragEnter: function(e, id) { /* override this */ },
19079
19080     /**
19081      * Code that executes immediately before the onDragOver event
19082      * @method b4DragOver
19083      * @private
19084      */
19085     b4DragOver: function(e) { },
19086
19087     /**
19088      * Abstract method called when this element is hovering over another
19089      * DragDrop obj
19090      * @method onDragOver
19091      * @param {Event} e the mousemove event
19092      * @param {String|DragDrop[]} id In POINT mode, the element
19093      * id this is hovering over.  In INTERSECT mode, an array of dd items
19094      * being hovered over.
19095      */
19096     onDragOver: function(e, id) { /* override this */ },
19097
19098     /**
19099      * Code that executes immediately before the onDragOut event
19100      * @method b4DragOut
19101      * @private
19102      */
19103     b4DragOut: function(e) { },
19104
19105     /**
19106      * Abstract method called when we are no longer hovering over an element
19107      * @method onDragOut
19108      * @param {Event} e the mousemove event
19109      * @param {String|DragDrop[]} id In POINT mode, the element
19110      * id this was hovering over.  In INTERSECT mode, an array of dd items
19111      * that the mouse is no longer over.
19112      */
19113     onDragOut: function(e, id) { /* override this */ },
19114
19115     /**
19116      * Code that executes immediately before the onDragDrop event
19117      * @method b4DragDrop
19118      * @private
19119      */
19120     b4DragDrop: function(e) { },
19121
19122     /**
19123      * Abstract method called when this item is dropped on another DragDrop
19124      * obj
19125      * @method onDragDrop
19126      * @param {Event} e the mouseup event
19127      * @param {String|DragDrop[]} id In POINT mode, the element
19128      * id this was dropped on.  In INTERSECT mode, an array of dd items this
19129      * was dropped on.
19130      */
19131     onDragDrop: function(e, id) { /* override this */ },
19132
19133     /**
19134      * Abstract method called when this item is dropped on an area with no
19135      * drop target
19136      * @method onInvalidDrop
19137      * @param {Event} e the mouseup event
19138      */
19139     onInvalidDrop: function(e) { /* override this */ },
19140
19141     /**
19142      * Code that executes immediately before the endDrag event
19143      * @method b4EndDrag
19144      * @private
19145      */
19146     b4EndDrag: function(e) { },
19147
19148     /**
19149      * Fired when we are done dragging the object
19150      * @method endDrag
19151      * @param {Event} e the mouseup event
19152      */
19153     endDrag: function(e) { /* override this */ },
19154
19155     /**
19156      * Code executed immediately before the onMouseDown event
19157      * @method b4MouseDown
19158      * @param {Event} e the mousedown event
19159      * @private
19160      */
19161     b4MouseDown: function(e) {  },
19162
19163     /**
19164      * Event handler that fires when a drag/drop obj gets a mousedown
19165      * @method onMouseDown
19166      * @param {Event} e the mousedown event
19167      */
19168     onMouseDown: function(e) { /* override this */ },
19169
19170     /**
19171      * Event handler that fires when a drag/drop obj gets a mouseup
19172      * @method onMouseUp
19173      * @param {Event} e the mouseup event
19174      */
19175     onMouseUp: function(e) { /* override this */ },
19176
19177     /**
19178      * Override the onAvailable method to do what is needed after the initial
19179      * position was determined.
19180      * @method onAvailable
19181      */
19182     onAvailable: function () {
19183     },
19184
19185     /*
19186      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
19187      * @type Object
19188      */
19189     defaultPadding : {left:0, right:0, top:0, bottom:0},
19190
19191     /*
19192      * Initializes the drag drop object's constraints to restrict movement to a certain element.
19193  *
19194  * Usage:
19195  <pre><code>
19196  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
19197                 { dragElId: "existingProxyDiv" });
19198  dd.startDrag = function(){
19199      this.constrainTo("parent-id");
19200  };
19201  </code></pre>
19202  * Or you can initalize it using the {@link Roo.Element} object:
19203  <pre><code>
19204  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
19205      startDrag : function(){
19206          this.constrainTo("parent-id");
19207      }
19208  });
19209  </code></pre>
19210      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
19211      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
19212      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
19213      * an object containing the sides to pad. For example: {right:10, bottom:10}
19214      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
19215      */
19216     constrainTo : function(constrainTo, pad, inContent){
19217         if(typeof pad == "number"){
19218             pad = {left: pad, right:pad, top:pad, bottom:pad};
19219         }
19220         pad = pad || this.defaultPadding;
19221         var b = Roo.get(this.getEl()).getBox();
19222         var ce = Roo.get(constrainTo);
19223         var s = ce.getScroll();
19224         var c, cd = ce.dom;
19225         if(cd == document.body){
19226             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
19227         }else{
19228             xy = ce.getXY();
19229             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
19230         }
19231
19232
19233         var topSpace = b.y - c.y;
19234         var leftSpace = b.x - c.x;
19235
19236         this.resetConstraints();
19237         this.setXConstraint(leftSpace - (pad.left||0), // left
19238                 c.width - leftSpace - b.width - (pad.right||0) //right
19239         );
19240         this.setYConstraint(topSpace - (pad.top||0), //top
19241                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
19242         );
19243     },
19244
19245     /**
19246      * Returns a reference to the linked element
19247      * @method getEl
19248      * @return {HTMLElement} the html element
19249      */
19250     getEl: function() {
19251         if (!this._domRef) {
19252             this._domRef = Roo.getDom(this.id);
19253         }
19254
19255         return this._domRef;
19256     },
19257
19258     /**
19259      * Returns a reference to the actual element to drag.  By default this is
19260      * the same as the html element, but it can be assigned to another
19261      * element. An example of this can be found in Roo.dd.DDProxy
19262      * @method getDragEl
19263      * @return {HTMLElement} the html element
19264      */
19265     getDragEl: function() {
19266         return Roo.getDom(this.dragElId);
19267     },
19268
19269     /**
19270      * Sets up the DragDrop object.  Must be called in the constructor of any
19271      * Roo.dd.DragDrop subclass
19272      * @method init
19273      * @param id the id of the linked element
19274      * @param {String} sGroup the group of related items
19275      * @param {object} config configuration attributes
19276      */
19277     init: function(id, sGroup, config) {
19278         this.initTarget(id, sGroup, config);
19279         if (!Roo.isTouch) {
19280             Event.on(this.id, "mousedown", this.handleMouseDown, this);
19281         }
19282         Event.on(this.id, "touchstart", this.handleMouseDown, this);
19283         // Event.on(this.id, "selectstart", Event.preventDefault);
19284     },
19285
19286     /**
19287      * Initializes Targeting functionality only... the object does not
19288      * get a mousedown handler.
19289      * @method initTarget
19290      * @param id the id of the linked element
19291      * @param {String} sGroup the group of related items
19292      * @param {object} config configuration attributes
19293      */
19294     initTarget: function(id, sGroup, config) {
19295
19296         // configuration attributes
19297         this.config = config || {};
19298
19299         // create a local reference to the drag and drop manager
19300         this.DDM = Roo.dd.DDM;
19301         // initialize the groups array
19302         this.groups = {};
19303
19304         // assume that we have an element reference instead of an id if the
19305         // parameter is not a string
19306         if (typeof id !== "string") {
19307             id = Roo.id(id);
19308         }
19309
19310         // set the id
19311         this.id = id;
19312
19313         // add to an interaction group
19314         this.addToGroup((sGroup) ? sGroup : "default");
19315
19316         // We don't want to register this as the handle with the manager
19317         // so we just set the id rather than calling the setter.
19318         this.handleElId = id;
19319
19320         // the linked element is the element that gets dragged by default
19321         this.setDragElId(id);
19322
19323         // by default, clicked anchors will not start drag operations.
19324         this.invalidHandleTypes = { A: "A" };
19325         this.invalidHandleIds = {};
19326         this.invalidHandleClasses = [];
19327
19328         this.applyConfig();
19329
19330         this.handleOnAvailable();
19331     },
19332
19333     /**
19334      * Applies the configuration parameters that were passed into the constructor.
19335      * This is supposed to happen at each level through the inheritance chain.  So
19336      * a DDProxy implentation will execute apply config on DDProxy, DD, and
19337      * DragDrop in order to get all of the parameters that are available in
19338      * each object.
19339      * @method applyConfig
19340      */
19341     applyConfig: function() {
19342
19343         // configurable properties:
19344         //    padding, isTarget, maintainOffset, primaryButtonOnly
19345         this.padding           = this.config.padding || [0, 0, 0, 0];
19346         this.isTarget          = (this.config.isTarget !== false);
19347         this.maintainOffset    = (this.config.maintainOffset);
19348         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
19349
19350     },
19351
19352     /**
19353      * Executed when the linked element is available
19354      * @method handleOnAvailable
19355      * @private
19356      */
19357     handleOnAvailable: function() {
19358         this.available = true;
19359         this.resetConstraints();
19360         this.onAvailable();
19361     },
19362
19363      /**
19364      * Configures the padding for the target zone in px.  Effectively expands
19365      * (or reduces) the virtual object size for targeting calculations.
19366      * Supports css-style shorthand; if only one parameter is passed, all sides
19367      * will have that padding, and if only two are passed, the top and bottom
19368      * will have the first param, the left and right the second.
19369      * @method setPadding
19370      * @param {int} iTop    Top pad
19371      * @param {int} iRight  Right pad
19372      * @param {int} iBot    Bot pad
19373      * @param {int} iLeft   Left pad
19374      */
19375     setPadding: function(iTop, iRight, iBot, iLeft) {
19376         // this.padding = [iLeft, iRight, iTop, iBot];
19377         if (!iRight && 0 !== iRight) {
19378             this.padding = [iTop, iTop, iTop, iTop];
19379         } else if (!iBot && 0 !== iBot) {
19380             this.padding = [iTop, iRight, iTop, iRight];
19381         } else {
19382             this.padding = [iTop, iRight, iBot, iLeft];
19383         }
19384     },
19385
19386     /**
19387      * Stores the initial placement of the linked element.
19388      * @method setInitialPosition
19389      * @param {int} diffX   the X offset, default 0
19390      * @param {int} diffY   the Y offset, default 0
19391      */
19392     setInitPosition: function(diffX, diffY) {
19393         var el = this.getEl();
19394
19395         if (!this.DDM.verifyEl(el)) {
19396             return;
19397         }
19398
19399         var dx = diffX || 0;
19400         var dy = diffY || 0;
19401
19402         var p = Dom.getXY( el );
19403
19404         this.initPageX = p[0] - dx;
19405         this.initPageY = p[1] - dy;
19406
19407         this.lastPageX = p[0];
19408         this.lastPageY = p[1];
19409
19410
19411         this.setStartPosition(p);
19412     },
19413
19414     /**
19415      * Sets the start position of the element.  This is set when the obj
19416      * is initialized, the reset when a drag is started.
19417      * @method setStartPosition
19418      * @param pos current position (from previous lookup)
19419      * @private
19420      */
19421     setStartPosition: function(pos) {
19422         var p = pos || Dom.getXY( this.getEl() );
19423         this.deltaSetXY = null;
19424
19425         this.startPageX = p[0];
19426         this.startPageY = p[1];
19427     },
19428
19429     /**
19430      * Add this instance to a group of related drag/drop objects.  All
19431      * instances belong to at least one group, and can belong to as many
19432      * groups as needed.
19433      * @method addToGroup
19434      * @param sGroup {string} the name of the group
19435      */
19436     addToGroup: function(sGroup) {
19437         this.groups[sGroup] = true;
19438         this.DDM.regDragDrop(this, sGroup);
19439     },
19440
19441     /**
19442      * Remove's this instance from the supplied interaction group
19443      * @method removeFromGroup
19444      * @param {string}  sGroup  The group to drop
19445      */
19446     removeFromGroup: function(sGroup) {
19447         if (this.groups[sGroup]) {
19448             delete this.groups[sGroup];
19449         }
19450
19451         this.DDM.removeDDFromGroup(this, sGroup);
19452     },
19453
19454     /**
19455      * Allows you to specify that an element other than the linked element
19456      * will be moved with the cursor during a drag
19457      * @method setDragElId
19458      * @param id {string} the id of the element that will be used to initiate the drag
19459      */
19460     setDragElId: function(id) {
19461         this.dragElId = id;
19462     },
19463
19464     /**
19465      * Allows you to specify a child of the linked element that should be
19466      * used to initiate the drag operation.  An example of this would be if
19467      * you have a content div with text and links.  Clicking anywhere in the
19468      * content area would normally start the drag operation.  Use this method
19469      * to specify that an element inside of the content div is the element
19470      * that starts the drag operation.
19471      * @method setHandleElId
19472      * @param id {string} the id of the element that will be used to
19473      * initiate the drag.
19474      */
19475     setHandleElId: function(id) {
19476         if (typeof id !== "string") {
19477             id = Roo.id(id);
19478         }
19479         this.handleElId = id;
19480         this.DDM.regHandle(this.id, id);
19481     },
19482
19483     /**
19484      * Allows you to set an element outside of the linked element as a drag
19485      * handle
19486      * @method setOuterHandleElId
19487      * @param id the id of the element that will be used to initiate the drag
19488      */
19489     setOuterHandleElId: function(id) {
19490         if (typeof id !== "string") {
19491             id = Roo.id(id);
19492         }
19493         Event.on(id, "mousedown",
19494                 this.handleMouseDown, this);
19495         this.setHandleElId(id);
19496
19497         this.hasOuterHandles = true;
19498     },
19499
19500     /**
19501      * Remove all drag and drop hooks for this element
19502      * @method unreg
19503      */
19504     unreg: function() {
19505         Event.un(this.id, "mousedown",
19506                 this.handleMouseDown);
19507         Event.un(this.id, "touchstart",
19508                 this.handleMouseDown);
19509         this._domRef = null;
19510         this.DDM._remove(this);
19511     },
19512
19513     destroy : function(){
19514         this.unreg();
19515     },
19516
19517     /**
19518      * Returns true if this instance is locked, or the drag drop mgr is locked
19519      * (meaning that all drag/drop is disabled on the page.)
19520      * @method isLocked
19521      * @return {boolean} true if this obj or all drag/drop is locked, else
19522      * false
19523      */
19524     isLocked: function() {
19525         return (this.DDM.isLocked() || this.locked);
19526     },
19527
19528     /**
19529      * Fired when this object is clicked
19530      * @method handleMouseDown
19531      * @param {Event} e
19532      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
19533      * @private
19534      */
19535     handleMouseDown: function(e, oDD){
19536      
19537         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
19538             //Roo.log('not touch/ button !=0');
19539             return;
19540         }
19541         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
19542             return; // double touch..
19543         }
19544         
19545
19546         if (this.isLocked()) {
19547             //Roo.log('locked');
19548             return;
19549         }
19550
19551         this.DDM.refreshCache(this.groups);
19552 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
19553         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
19554         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
19555             //Roo.log('no outer handes or not over target');
19556                 // do nothing.
19557         } else {
19558 //            Roo.log('check validator');
19559             if (this.clickValidator(e)) {
19560 //                Roo.log('validate success');
19561                 // set the initial element position
19562                 this.setStartPosition();
19563
19564
19565                 this.b4MouseDown(e);
19566                 this.onMouseDown(e);
19567
19568                 this.DDM.handleMouseDown(e, this);
19569
19570                 this.DDM.stopEvent(e);
19571             } else {
19572
19573
19574             }
19575         }
19576     },
19577
19578     clickValidator: function(e) {
19579         var target = e.getTarget();
19580         return ( this.isValidHandleChild(target) &&
19581                     (this.id == this.handleElId ||
19582                         this.DDM.handleWasClicked(target, this.id)) );
19583     },
19584
19585     /**
19586      * Allows you to specify a tag name that should not start a drag operation
19587      * when clicked.  This is designed to facilitate embedding links within a
19588      * drag handle that do something other than start the drag.
19589      * @method addInvalidHandleType
19590      * @param {string} tagName the type of element to exclude
19591      */
19592     addInvalidHandleType: function(tagName) {
19593         var type = tagName.toUpperCase();
19594         this.invalidHandleTypes[type] = type;
19595     },
19596
19597     /**
19598      * Lets you to specify an element id for a child of a drag handle
19599      * that should not initiate a drag
19600      * @method addInvalidHandleId
19601      * @param {string} id the element id of the element you wish to ignore
19602      */
19603     addInvalidHandleId: function(id) {
19604         if (typeof id !== "string") {
19605             id = Roo.id(id);
19606         }
19607         this.invalidHandleIds[id] = id;
19608     },
19609
19610     /**
19611      * Lets you specify a css class of elements that will not initiate a drag
19612      * @method addInvalidHandleClass
19613      * @param {string} cssClass the class of the elements you wish to ignore
19614      */
19615     addInvalidHandleClass: function(cssClass) {
19616         this.invalidHandleClasses.push(cssClass);
19617     },
19618
19619     /**
19620      * Unsets an excluded tag name set by addInvalidHandleType
19621      * @method removeInvalidHandleType
19622      * @param {string} tagName the type of element to unexclude
19623      */
19624     removeInvalidHandleType: function(tagName) {
19625         var type = tagName.toUpperCase();
19626         // this.invalidHandleTypes[type] = null;
19627         delete this.invalidHandleTypes[type];
19628     },
19629
19630     /**
19631      * Unsets an invalid handle id
19632      * @method removeInvalidHandleId
19633      * @param {string} id the id of the element to re-enable
19634      */
19635     removeInvalidHandleId: function(id) {
19636         if (typeof id !== "string") {
19637             id = Roo.id(id);
19638         }
19639         delete this.invalidHandleIds[id];
19640     },
19641
19642     /**
19643      * Unsets an invalid css class
19644      * @method removeInvalidHandleClass
19645      * @param {string} cssClass the class of the element(s) you wish to
19646      * re-enable
19647      */
19648     removeInvalidHandleClass: function(cssClass) {
19649         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19650             if (this.invalidHandleClasses[i] == cssClass) {
19651                 delete this.invalidHandleClasses[i];
19652             }
19653         }
19654     },
19655
19656     /**
19657      * Checks the tag exclusion list to see if this click should be ignored
19658      * @method isValidHandleChild
19659      * @param {HTMLElement} node the HTMLElement to evaluate
19660      * @return {boolean} true if this is a valid tag type, false if not
19661      */
19662     isValidHandleChild: function(node) {
19663
19664         var valid = true;
19665         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19666         var nodeName;
19667         try {
19668             nodeName = node.nodeName.toUpperCase();
19669         } catch(e) {
19670             nodeName = node.nodeName;
19671         }
19672         valid = valid && !this.invalidHandleTypes[nodeName];
19673         valid = valid && !this.invalidHandleIds[node.id];
19674
19675         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19676             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19677         }
19678
19679
19680         return valid;
19681
19682     },
19683
19684     /**
19685      * Create the array of horizontal tick marks if an interval was specified
19686      * in setXConstraint().
19687      * @method setXTicks
19688      * @private
19689      */
19690     setXTicks: function(iStartX, iTickSize) {
19691         this.xTicks = [];
19692         this.xTickSize = iTickSize;
19693
19694         var tickMap = {};
19695
19696         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19697             if (!tickMap[i]) {
19698                 this.xTicks[this.xTicks.length] = i;
19699                 tickMap[i] = true;
19700             }
19701         }
19702
19703         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19704             if (!tickMap[i]) {
19705                 this.xTicks[this.xTicks.length] = i;
19706                 tickMap[i] = true;
19707             }
19708         }
19709
19710         this.xTicks.sort(this.DDM.numericSort) ;
19711     },
19712
19713     /**
19714      * Create the array of vertical tick marks if an interval was specified in
19715      * setYConstraint().
19716      * @method setYTicks
19717      * @private
19718      */
19719     setYTicks: function(iStartY, iTickSize) {
19720         this.yTicks = [];
19721         this.yTickSize = iTickSize;
19722
19723         var tickMap = {};
19724
19725         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19726             if (!tickMap[i]) {
19727                 this.yTicks[this.yTicks.length] = i;
19728                 tickMap[i] = true;
19729             }
19730         }
19731
19732         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19733             if (!tickMap[i]) {
19734                 this.yTicks[this.yTicks.length] = i;
19735                 tickMap[i] = true;
19736             }
19737         }
19738
19739         this.yTicks.sort(this.DDM.numericSort) ;
19740     },
19741
19742     /**
19743      * By default, the element can be dragged any place on the screen.  Use
19744      * this method to limit the horizontal travel of the element.  Pass in
19745      * 0,0 for the parameters if you want to lock the drag to the y axis.
19746      * @method setXConstraint
19747      * @param {int} iLeft the number of pixels the element can move to the left
19748      * @param {int} iRight the number of pixels the element can move to the
19749      * right
19750      * @param {int} iTickSize optional parameter for specifying that the
19751      * element
19752      * should move iTickSize pixels at a time.
19753      */
19754     setXConstraint: function(iLeft, iRight, iTickSize) {
19755         this.leftConstraint = iLeft;
19756         this.rightConstraint = iRight;
19757
19758         this.minX = this.initPageX - iLeft;
19759         this.maxX = this.initPageX + iRight;
19760         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19761
19762         this.constrainX = true;
19763     },
19764
19765     /**
19766      * Clears any constraints applied to this instance.  Also clears ticks
19767      * since they can't exist independent of a constraint at this time.
19768      * @method clearConstraints
19769      */
19770     clearConstraints: function() {
19771         this.constrainX = false;
19772         this.constrainY = false;
19773         this.clearTicks();
19774     },
19775
19776     /**
19777      * Clears any tick interval defined for this instance
19778      * @method clearTicks
19779      */
19780     clearTicks: function() {
19781         this.xTicks = null;
19782         this.yTicks = null;
19783         this.xTickSize = 0;
19784         this.yTickSize = 0;
19785     },
19786
19787     /**
19788      * By default, the element can be dragged any place on the screen.  Set
19789      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19790      * parameters if you want to lock the drag to the x axis.
19791      * @method setYConstraint
19792      * @param {int} iUp the number of pixels the element can move up
19793      * @param {int} iDown the number of pixels the element can move down
19794      * @param {int} iTickSize optional parameter for specifying that the
19795      * element should move iTickSize pixels at a time.
19796      */
19797     setYConstraint: function(iUp, iDown, iTickSize) {
19798         this.topConstraint = iUp;
19799         this.bottomConstraint = iDown;
19800
19801         this.minY = this.initPageY - iUp;
19802         this.maxY = this.initPageY + iDown;
19803         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19804
19805         this.constrainY = true;
19806
19807     },
19808
19809     /**
19810      * resetConstraints must be called if you manually reposition a dd element.
19811      * @method resetConstraints
19812      * @param {boolean} maintainOffset
19813      */
19814     resetConstraints: function() {
19815
19816
19817         // Maintain offsets if necessary
19818         if (this.initPageX || this.initPageX === 0) {
19819             // figure out how much this thing has moved
19820             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19821             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19822
19823             this.setInitPosition(dx, dy);
19824
19825         // This is the first time we have detected the element's position
19826         } else {
19827             this.setInitPosition();
19828         }
19829
19830         if (this.constrainX) {
19831             this.setXConstraint( this.leftConstraint,
19832                                  this.rightConstraint,
19833                                  this.xTickSize        );
19834         }
19835
19836         if (this.constrainY) {
19837             this.setYConstraint( this.topConstraint,
19838                                  this.bottomConstraint,
19839                                  this.yTickSize         );
19840         }
19841     },
19842
19843     /**
19844      * Normally the drag element is moved pixel by pixel, but we can specify
19845      * that it move a number of pixels at a time.  This method resolves the
19846      * location when we have it set up like this.
19847      * @method getTick
19848      * @param {int} val where we want to place the object
19849      * @param {int[]} tickArray sorted array of valid points
19850      * @return {int} the closest tick
19851      * @private
19852      */
19853     getTick: function(val, tickArray) {
19854
19855         if (!tickArray) {
19856             // If tick interval is not defined, it is effectively 1 pixel,
19857             // so we return the value passed to us.
19858             return val;
19859         } else if (tickArray[0] >= val) {
19860             // The value is lower than the first tick, so we return the first
19861             // tick.
19862             return tickArray[0];
19863         } else {
19864             for (var i=0, len=tickArray.length; i<len; ++i) {
19865                 var next = i + 1;
19866                 if (tickArray[next] && tickArray[next] >= val) {
19867                     var diff1 = val - tickArray[i];
19868                     var diff2 = tickArray[next] - val;
19869                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19870                 }
19871             }
19872
19873             // The value is larger than the last tick, so we return the last
19874             // tick.
19875             return tickArray[tickArray.length - 1];
19876         }
19877     },
19878
19879     /**
19880      * toString method
19881      * @method toString
19882      * @return {string} string representation of the dd obj
19883      */
19884     toString: function() {
19885         return ("DragDrop " + this.id);
19886     }
19887
19888 });
19889
19890 })();
19891 /*
19892  * Based on:
19893  * Ext JS Library 1.1.1
19894  * Copyright(c) 2006-2007, Ext JS, LLC.
19895  *
19896  * Originally Released Under LGPL - original licence link has changed is not relivant.
19897  *
19898  * Fork - LGPL
19899  * <script type="text/javascript">
19900  */
19901
19902
19903 /**
19904  * The drag and drop utility provides a framework for building drag and drop
19905  * applications.  In addition to enabling drag and drop for specific elements,
19906  * the drag and drop elements are tracked by the manager class, and the
19907  * interactions between the various elements are tracked during the drag and
19908  * the implementing code is notified about these important moments.
19909  */
19910
19911 // Only load the library once.  Rewriting the manager class would orphan
19912 // existing drag and drop instances.
19913 if (!Roo.dd.DragDropMgr) {
19914
19915 /**
19916  * @class Roo.dd.DragDropMgr
19917  * DragDropMgr is a singleton that tracks the element interaction for
19918  * all DragDrop items in the window.  Generally, you will not call
19919  * this class directly, but it does have helper methods that could
19920  * be useful in your DragDrop implementations.
19921  * @singleton
19922  */
19923 Roo.dd.DragDropMgr = function() {
19924
19925     var Event = Roo.EventManager;
19926
19927     return {
19928
19929         /**
19930          * Two dimensional Array of registered DragDrop objects.  The first
19931          * dimension is the DragDrop item group, the second the DragDrop
19932          * object.
19933          * @property ids
19934          * @type {string: string}
19935          * @private
19936          * @static
19937          */
19938         ids: {},
19939
19940         /**
19941          * Array of element ids defined as drag handles.  Used to determine
19942          * if the element that generated the mousedown event is actually the
19943          * handle and not the html element itself.
19944          * @property handleIds
19945          * @type {string: string}
19946          * @private
19947          * @static
19948          */
19949         handleIds: {},
19950
19951         /**
19952          * the DragDrop object that is currently being dragged
19953          * @property dragCurrent
19954          * @type DragDrop
19955          * @private
19956          * @static
19957          **/
19958         dragCurrent: null,
19959
19960         /**
19961          * the DragDrop object(s) that are being hovered over
19962          * @property dragOvers
19963          * @type Array
19964          * @private
19965          * @static
19966          */
19967         dragOvers: {},
19968
19969         /**
19970          * the X distance between the cursor and the object being dragged
19971          * @property deltaX
19972          * @type int
19973          * @private
19974          * @static
19975          */
19976         deltaX: 0,
19977
19978         /**
19979          * the Y distance between the cursor and the object being dragged
19980          * @property deltaY
19981          * @type int
19982          * @private
19983          * @static
19984          */
19985         deltaY: 0,
19986
19987         /**
19988          * Flag to determine if we should prevent the default behavior of the
19989          * events we define. By default this is true, but this can be set to
19990          * false if you need the default behavior (not recommended)
19991          * @property preventDefault
19992          * @type boolean
19993          * @static
19994          */
19995         preventDefault: true,
19996
19997         /**
19998          * Flag to determine if we should stop the propagation of the events
19999          * we generate. This is true by default but you may want to set it to
20000          * false if the html element contains other features that require the
20001          * mouse click.
20002          * @property stopPropagation
20003          * @type boolean
20004          * @static
20005          */
20006         stopPropagation: true,
20007
20008         /**
20009          * Internal flag that is set to true when drag and drop has been
20010          * intialized
20011          * @property initialized
20012          * @private
20013          * @static
20014          */
20015         initalized: false,
20016
20017         /**
20018          * All drag and drop can be disabled.
20019          * @property locked
20020          * @private
20021          * @static
20022          */
20023         locked: false,
20024
20025         /**
20026          * Called the first time an element is registered.
20027          * @method init
20028          * @private
20029          * @static
20030          */
20031         init: function() {
20032             this.initialized = true;
20033         },
20034
20035         /**
20036          * In point mode, drag and drop interaction is defined by the
20037          * location of the cursor during the drag/drop
20038          * @property POINT
20039          * @type int
20040          * @static
20041          */
20042         POINT: 0,
20043
20044         /**
20045          * In intersect mode, drag and drop interactio nis defined by the
20046          * overlap of two or more drag and drop objects.
20047          * @property INTERSECT
20048          * @type int
20049          * @static
20050          */
20051         INTERSECT: 1,
20052
20053         /**
20054          * The current drag and drop mode.  Default: POINT
20055          * @property mode
20056          * @type int
20057          * @static
20058          */
20059         mode: 0,
20060
20061         /**
20062          * Runs method on all drag and drop objects
20063          * @method _execOnAll
20064          * @private
20065          * @static
20066          */
20067         _execOnAll: function(sMethod, args) {
20068             for (var i in this.ids) {
20069                 for (var j in this.ids[i]) {
20070                     var oDD = this.ids[i][j];
20071                     if (! this.isTypeOfDD(oDD)) {
20072                         continue;
20073                     }
20074                     oDD[sMethod].apply(oDD, args);
20075                 }
20076             }
20077         },
20078
20079         /**
20080          * Drag and drop initialization.  Sets up the global event handlers
20081          * @method _onLoad
20082          * @private
20083          * @static
20084          */
20085         _onLoad: function() {
20086
20087             this.init();
20088
20089             if (!Roo.isTouch) {
20090                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20091                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20092             }
20093             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20094             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20095             
20096             Event.on(window,   "unload",    this._onUnload, this, true);
20097             Event.on(window,   "resize",    this._onResize, this, true);
20098             // Event.on(window,   "mouseout",    this._test);
20099
20100         },
20101
20102         /**
20103          * Reset constraints on all drag and drop objs
20104          * @method _onResize
20105          * @private
20106          * @static
20107          */
20108         _onResize: function(e) {
20109             this._execOnAll("resetConstraints", []);
20110         },
20111
20112         /**
20113          * Lock all drag and drop functionality
20114          * @method lock
20115          * @static
20116          */
20117         lock: function() { this.locked = true; },
20118
20119         /**
20120          * Unlock all drag and drop functionality
20121          * @method unlock
20122          * @static
20123          */
20124         unlock: function() { this.locked = false; },
20125
20126         /**
20127          * Is drag and drop locked?
20128          * @method isLocked
20129          * @return {boolean} True if drag and drop is locked, false otherwise.
20130          * @static
20131          */
20132         isLocked: function() { return this.locked; },
20133
20134         /**
20135          * Location cache that is set for all drag drop objects when a drag is
20136          * initiated, cleared when the drag is finished.
20137          * @property locationCache
20138          * @private
20139          * @static
20140          */
20141         locationCache: {},
20142
20143         /**
20144          * Set useCache to false if you want to force object the lookup of each
20145          * drag and drop linked element constantly during a drag.
20146          * @property useCache
20147          * @type boolean
20148          * @static
20149          */
20150         useCache: true,
20151
20152         /**
20153          * The number of pixels that the mouse needs to move after the
20154          * mousedown before the drag is initiated.  Default=3;
20155          * @property clickPixelThresh
20156          * @type int
20157          * @static
20158          */
20159         clickPixelThresh: 3,
20160
20161         /**
20162          * The number of milliseconds after the mousedown event to initiate the
20163          * drag if we don't get a mouseup event. Default=1000
20164          * @property clickTimeThresh
20165          * @type int
20166          * @static
20167          */
20168         clickTimeThresh: 350,
20169
20170         /**
20171          * Flag that indicates that either the drag pixel threshold or the
20172          * mousdown time threshold has been met
20173          * @property dragThreshMet
20174          * @type boolean
20175          * @private
20176          * @static
20177          */
20178         dragThreshMet: false,
20179
20180         /**
20181          * Timeout used for the click time threshold
20182          * @property clickTimeout
20183          * @type Object
20184          * @private
20185          * @static
20186          */
20187         clickTimeout: null,
20188
20189         /**
20190          * The X position of the mousedown event stored for later use when a
20191          * drag threshold is met.
20192          * @property startX
20193          * @type int
20194          * @private
20195          * @static
20196          */
20197         startX: 0,
20198
20199         /**
20200          * The Y position of the mousedown event stored for later use when a
20201          * drag threshold is met.
20202          * @property startY
20203          * @type int
20204          * @private
20205          * @static
20206          */
20207         startY: 0,
20208
20209         /**
20210          * Each DragDrop instance must be registered with the DragDropMgr.
20211          * This is executed in DragDrop.init()
20212          * @method regDragDrop
20213          * @param {DragDrop} oDD the DragDrop object to register
20214          * @param {String} sGroup the name of the group this element belongs to
20215          * @static
20216          */
20217         regDragDrop: function(oDD, sGroup) {
20218             if (!this.initialized) { this.init(); }
20219
20220             if (!this.ids[sGroup]) {
20221                 this.ids[sGroup] = {};
20222             }
20223             this.ids[sGroup][oDD.id] = oDD;
20224         },
20225
20226         /**
20227          * Removes the supplied dd instance from the supplied group. Executed
20228          * by DragDrop.removeFromGroup, so don't call this function directly.
20229          * @method removeDDFromGroup
20230          * @private
20231          * @static
20232          */
20233         removeDDFromGroup: function(oDD, sGroup) {
20234             if (!this.ids[sGroup]) {
20235                 this.ids[sGroup] = {};
20236             }
20237
20238             var obj = this.ids[sGroup];
20239             if (obj && obj[oDD.id]) {
20240                 delete obj[oDD.id];
20241             }
20242         },
20243
20244         /**
20245          * Unregisters a drag and drop item.  This is executed in
20246          * DragDrop.unreg, use that method instead of calling this directly.
20247          * @method _remove
20248          * @private
20249          * @static
20250          */
20251         _remove: function(oDD) {
20252             for (var g in oDD.groups) {
20253                 if (g && this.ids[g][oDD.id]) {
20254                     delete this.ids[g][oDD.id];
20255                 }
20256             }
20257             delete this.handleIds[oDD.id];
20258         },
20259
20260         /**
20261          * Each DragDrop handle element must be registered.  This is done
20262          * automatically when executing DragDrop.setHandleElId()
20263          * @method regHandle
20264          * @param {String} sDDId the DragDrop id this element is a handle for
20265          * @param {String} sHandleId the id of the element that is the drag
20266          * handle
20267          * @static
20268          */
20269         regHandle: function(sDDId, sHandleId) {
20270             if (!this.handleIds[sDDId]) {
20271                 this.handleIds[sDDId] = {};
20272             }
20273             this.handleIds[sDDId][sHandleId] = sHandleId;
20274         },
20275
20276         /**
20277          * Utility function to determine if a given element has been
20278          * registered as a drag drop item.
20279          * @method isDragDrop
20280          * @param {String} id the element id to check
20281          * @return {boolean} true if this element is a DragDrop item,
20282          * false otherwise
20283          * @static
20284          */
20285         isDragDrop: function(id) {
20286             return ( this.getDDById(id) ) ? true : false;
20287         },
20288
20289         /**
20290          * Returns the drag and drop instances that are in all groups the
20291          * passed in instance belongs to.
20292          * @method getRelated
20293          * @param {DragDrop} p_oDD the obj to get related data for
20294          * @param {boolean} bTargetsOnly if true, only return targetable objs
20295          * @return {DragDrop[]} the related instances
20296          * @static
20297          */
20298         getRelated: function(p_oDD, bTargetsOnly) {
20299             var oDDs = [];
20300             for (var i in p_oDD.groups) {
20301                 for (j in this.ids[i]) {
20302                     var dd = this.ids[i][j];
20303                     if (! this.isTypeOfDD(dd)) {
20304                         continue;
20305                     }
20306                     if (!bTargetsOnly || dd.isTarget) {
20307                         oDDs[oDDs.length] = dd;
20308                     }
20309                 }
20310             }
20311
20312             return oDDs;
20313         },
20314
20315         /**
20316          * Returns true if the specified dd target is a legal target for
20317          * the specifice drag obj
20318          * @method isLegalTarget
20319          * @param {DragDrop} the drag obj
20320          * @param {DragDrop} the target
20321          * @return {boolean} true if the target is a legal target for the
20322          * dd obj
20323          * @static
20324          */
20325         isLegalTarget: function (oDD, oTargetDD) {
20326             var targets = this.getRelated(oDD, true);
20327             for (var i=0, len=targets.length;i<len;++i) {
20328                 if (targets[i].id == oTargetDD.id) {
20329                     return true;
20330                 }
20331             }
20332
20333             return false;
20334         },
20335
20336         /**
20337          * My goal is to be able to transparently determine if an object is
20338          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
20339          * returns "object", oDD.constructor.toString() always returns
20340          * "DragDrop" and not the name of the subclass.  So for now it just
20341          * evaluates a well-known variable in DragDrop.
20342          * @method isTypeOfDD
20343          * @param {Object} the object to evaluate
20344          * @return {boolean} true if typeof oDD = DragDrop
20345          * @static
20346          */
20347         isTypeOfDD: function (oDD) {
20348             return (oDD && oDD.__ygDragDrop);
20349         },
20350
20351         /**
20352          * Utility function to determine if a given element has been
20353          * registered as a drag drop handle for the given Drag Drop object.
20354          * @method isHandle
20355          * @param {String} id the element id to check
20356          * @return {boolean} true if this element is a DragDrop handle, false
20357          * otherwise
20358          * @static
20359          */
20360         isHandle: function(sDDId, sHandleId) {
20361             return ( this.handleIds[sDDId] &&
20362                             this.handleIds[sDDId][sHandleId] );
20363         },
20364
20365         /**
20366          * Returns the DragDrop instance for a given id
20367          * @method getDDById
20368          * @param {String} id the id of the DragDrop object
20369          * @return {DragDrop} the drag drop object, null if it is not found
20370          * @static
20371          */
20372         getDDById: function(id) {
20373             for (var i in this.ids) {
20374                 if (this.ids[i][id]) {
20375                     return this.ids[i][id];
20376                 }
20377             }
20378             return null;
20379         },
20380
20381         /**
20382          * Fired after a registered DragDrop object gets the mousedown event.
20383          * Sets up the events required to track the object being dragged
20384          * @method handleMouseDown
20385          * @param {Event} e the event
20386          * @param oDD the DragDrop object being dragged
20387          * @private
20388          * @static
20389          */
20390         handleMouseDown: function(e, oDD) {
20391             if(Roo.QuickTips){
20392                 Roo.QuickTips.disable();
20393             }
20394             this.currentTarget = e.getTarget();
20395
20396             this.dragCurrent = oDD;
20397
20398             var el = oDD.getEl();
20399
20400             // track start position
20401             this.startX = e.getPageX();
20402             this.startY = e.getPageY();
20403
20404             this.deltaX = this.startX - el.offsetLeft;
20405             this.deltaY = this.startY - el.offsetTop;
20406
20407             this.dragThreshMet = false;
20408
20409             this.clickTimeout = setTimeout(
20410                     function() {
20411                         var DDM = Roo.dd.DDM;
20412                         DDM.startDrag(DDM.startX, DDM.startY);
20413                     },
20414                     this.clickTimeThresh );
20415         },
20416
20417         /**
20418          * Fired when either the drag pixel threshol or the mousedown hold
20419          * time threshold has been met.
20420          * @method startDrag
20421          * @param x {int} the X position of the original mousedown
20422          * @param y {int} the Y position of the original mousedown
20423          * @static
20424          */
20425         startDrag: function(x, y) {
20426             clearTimeout(this.clickTimeout);
20427             if (this.dragCurrent) {
20428                 this.dragCurrent.b4StartDrag(x, y);
20429                 this.dragCurrent.startDrag(x, y);
20430             }
20431             this.dragThreshMet = true;
20432         },
20433
20434         /**
20435          * Internal function to handle the mouseup event.  Will be invoked
20436          * from the context of the document.
20437          * @method handleMouseUp
20438          * @param {Event} e the event
20439          * @private
20440          * @static
20441          */
20442         handleMouseUp: function(e) {
20443
20444             if(Roo.QuickTips){
20445                 Roo.QuickTips.enable();
20446             }
20447             if (! this.dragCurrent) {
20448                 return;
20449             }
20450
20451             clearTimeout(this.clickTimeout);
20452
20453             if (this.dragThreshMet) {
20454                 this.fireEvents(e, true);
20455             } else {
20456             }
20457
20458             this.stopDrag(e);
20459
20460             this.stopEvent(e);
20461         },
20462
20463         /**
20464          * Utility to stop event propagation and event default, if these
20465          * features are turned on.
20466          * @method stopEvent
20467          * @param {Event} e the event as returned by this.getEvent()
20468          * @static
20469          */
20470         stopEvent: function(e){
20471             if(this.stopPropagation) {
20472                 e.stopPropagation();
20473             }
20474
20475             if (this.preventDefault) {
20476                 e.preventDefault();
20477             }
20478         },
20479
20480         /**
20481          * Internal function to clean up event handlers after the drag
20482          * operation is complete
20483          * @method stopDrag
20484          * @param {Event} e the event
20485          * @private
20486          * @static
20487          */
20488         stopDrag: function(e) {
20489             // Fire the drag end event for the item that was dragged
20490             if (this.dragCurrent) {
20491                 if (this.dragThreshMet) {
20492                     this.dragCurrent.b4EndDrag(e);
20493                     this.dragCurrent.endDrag(e);
20494                 }
20495
20496                 this.dragCurrent.onMouseUp(e);
20497             }
20498
20499             this.dragCurrent = null;
20500             this.dragOvers = {};
20501         },
20502
20503         /**
20504          * Internal function to handle the mousemove event.  Will be invoked
20505          * from the context of the html element.
20506          *
20507          * @TODO figure out what we can do about mouse events lost when the
20508          * user drags objects beyond the window boundary.  Currently we can
20509          * detect this in internet explorer by verifying that the mouse is
20510          * down during the mousemove event.  Firefox doesn't give us the
20511          * button state on the mousemove event.
20512          * @method handleMouseMove
20513          * @param {Event} e the event
20514          * @private
20515          * @static
20516          */
20517         handleMouseMove: function(e) {
20518             if (! this.dragCurrent) {
20519                 return true;
20520             }
20521
20522             // var button = e.which || e.button;
20523
20524             // check for IE mouseup outside of page boundary
20525             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
20526                 this.stopEvent(e);
20527                 return this.handleMouseUp(e);
20528             }
20529
20530             if (!this.dragThreshMet) {
20531                 var diffX = Math.abs(this.startX - e.getPageX());
20532                 var diffY = Math.abs(this.startY - e.getPageY());
20533                 if (diffX > this.clickPixelThresh ||
20534                             diffY > this.clickPixelThresh) {
20535                     this.startDrag(this.startX, this.startY);
20536                 }
20537             }
20538
20539             if (this.dragThreshMet) {
20540                 this.dragCurrent.b4Drag(e);
20541                 this.dragCurrent.onDrag(e);
20542                 if(!this.dragCurrent.moveOnly){
20543                     this.fireEvents(e, false);
20544                 }
20545             }
20546
20547             this.stopEvent(e);
20548
20549             return true;
20550         },
20551
20552         /**
20553          * Iterates over all of the DragDrop elements to find ones we are
20554          * hovering over or dropping on
20555          * @method fireEvents
20556          * @param {Event} e the event
20557          * @param {boolean} isDrop is this a drop op or a mouseover op?
20558          * @private
20559          * @static
20560          */
20561         fireEvents: function(e, isDrop) {
20562             var dc = this.dragCurrent;
20563
20564             // If the user did the mouse up outside of the window, we could
20565             // get here even though we have ended the drag.
20566             if (!dc || dc.isLocked()) {
20567                 return;
20568             }
20569
20570             var pt = e.getPoint();
20571
20572             // cache the previous dragOver array
20573             var oldOvers = [];
20574
20575             var outEvts   = [];
20576             var overEvts  = [];
20577             var dropEvts  = [];
20578             var enterEvts = [];
20579
20580             // Check to see if the object(s) we were hovering over is no longer
20581             // being hovered over so we can fire the onDragOut event
20582             for (var i in this.dragOvers) {
20583
20584                 var ddo = this.dragOvers[i];
20585
20586                 if (! this.isTypeOfDD(ddo)) {
20587                     continue;
20588                 }
20589
20590                 if (! this.isOverTarget(pt, ddo, this.mode)) {
20591                     outEvts.push( ddo );
20592                 }
20593
20594                 oldOvers[i] = true;
20595                 delete this.dragOvers[i];
20596             }
20597
20598             for (var sGroup in dc.groups) {
20599
20600                 if ("string" != typeof sGroup) {
20601                     continue;
20602                 }
20603
20604                 for (i in this.ids[sGroup]) {
20605                     var oDD = this.ids[sGroup][i];
20606                     if (! this.isTypeOfDD(oDD)) {
20607                         continue;
20608                     }
20609
20610                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
20611                         if (this.isOverTarget(pt, oDD, this.mode)) {
20612                             // look for drop interactions
20613                             if (isDrop) {
20614                                 dropEvts.push( oDD );
20615                             // look for drag enter and drag over interactions
20616                             } else {
20617
20618                                 // initial drag over: dragEnter fires
20619                                 if (!oldOvers[oDD.id]) {
20620                                     enterEvts.push( oDD );
20621                                 // subsequent drag overs: dragOver fires
20622                                 } else {
20623                                     overEvts.push( oDD );
20624                                 }
20625
20626                                 this.dragOvers[oDD.id] = oDD;
20627                             }
20628                         }
20629                     }
20630                 }
20631             }
20632
20633             if (this.mode) {
20634                 if (outEvts.length) {
20635                     dc.b4DragOut(e, outEvts);
20636                     dc.onDragOut(e, outEvts);
20637                 }
20638
20639                 if (enterEvts.length) {
20640                     dc.onDragEnter(e, enterEvts);
20641                 }
20642
20643                 if (overEvts.length) {
20644                     dc.b4DragOver(e, overEvts);
20645                     dc.onDragOver(e, overEvts);
20646                 }
20647
20648                 if (dropEvts.length) {
20649                     dc.b4DragDrop(e, dropEvts);
20650                     dc.onDragDrop(e, dropEvts);
20651                 }
20652
20653             } else {
20654                 // fire dragout events
20655                 var len = 0;
20656                 for (i=0, len=outEvts.length; i<len; ++i) {
20657                     dc.b4DragOut(e, outEvts[i].id);
20658                     dc.onDragOut(e, outEvts[i].id);
20659                 }
20660
20661                 // fire enter events
20662                 for (i=0,len=enterEvts.length; i<len; ++i) {
20663                     // dc.b4DragEnter(e, oDD.id);
20664                     dc.onDragEnter(e, enterEvts[i].id);
20665                 }
20666
20667                 // fire over events
20668                 for (i=0,len=overEvts.length; i<len; ++i) {
20669                     dc.b4DragOver(e, overEvts[i].id);
20670                     dc.onDragOver(e, overEvts[i].id);
20671                 }
20672
20673                 // fire drop events
20674                 for (i=0, len=dropEvts.length; i<len; ++i) {
20675                     dc.b4DragDrop(e, dropEvts[i].id);
20676                     dc.onDragDrop(e, dropEvts[i].id);
20677                 }
20678
20679             }
20680
20681             // notify about a drop that did not find a target
20682             if (isDrop && !dropEvts.length) {
20683                 dc.onInvalidDrop(e);
20684             }
20685
20686         },
20687
20688         /**
20689          * Helper function for getting the best match from the list of drag
20690          * and drop objects returned by the drag and drop events when we are
20691          * in INTERSECT mode.  It returns either the first object that the
20692          * cursor is over, or the object that has the greatest overlap with
20693          * the dragged element.
20694          * @method getBestMatch
20695          * @param  {DragDrop[]} dds The array of drag and drop objects
20696          * targeted
20697          * @return {DragDrop}       The best single match
20698          * @static
20699          */
20700         getBestMatch: function(dds) {
20701             var winner = null;
20702             // Return null if the input is not what we expect
20703             //if (!dds || !dds.length || dds.length == 0) {
20704                // winner = null;
20705             // If there is only one item, it wins
20706             //} else if (dds.length == 1) {
20707
20708             var len = dds.length;
20709
20710             if (len == 1) {
20711                 winner = dds[0];
20712             } else {
20713                 // Loop through the targeted items
20714                 for (var i=0; i<len; ++i) {
20715                     var dd = dds[i];
20716                     // If the cursor is over the object, it wins.  If the
20717                     // cursor is over multiple matches, the first one we come
20718                     // to wins.
20719                     if (dd.cursorIsOver) {
20720                         winner = dd;
20721                         break;
20722                     // Otherwise the object with the most overlap wins
20723                     } else {
20724                         if (!winner ||
20725                             winner.overlap.getArea() < dd.overlap.getArea()) {
20726                             winner = dd;
20727                         }
20728                     }
20729                 }
20730             }
20731
20732             return winner;
20733         },
20734
20735         /**
20736          * Refreshes the cache of the top-left and bottom-right points of the
20737          * drag and drop objects in the specified group(s).  This is in the
20738          * format that is stored in the drag and drop instance, so typical
20739          * usage is:
20740          * <code>
20741          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20742          * </code>
20743          * Alternatively:
20744          * <code>
20745          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20746          * </code>
20747          * @TODO this really should be an indexed array.  Alternatively this
20748          * method could accept both.
20749          * @method refreshCache
20750          * @param {Object} groups an associative array of groups to refresh
20751          * @static
20752          */
20753         refreshCache: function(groups) {
20754             for (var sGroup in groups) {
20755                 if ("string" != typeof sGroup) {
20756                     continue;
20757                 }
20758                 for (var i in this.ids[sGroup]) {
20759                     var oDD = this.ids[sGroup][i];
20760
20761                     if (this.isTypeOfDD(oDD)) {
20762                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20763                         var loc = this.getLocation(oDD);
20764                         if (loc) {
20765                             this.locationCache[oDD.id] = loc;
20766                         } else {
20767                             delete this.locationCache[oDD.id];
20768                             // this will unregister the drag and drop object if
20769                             // the element is not in a usable state
20770                             // oDD.unreg();
20771                         }
20772                     }
20773                 }
20774             }
20775         },
20776
20777         /**
20778          * This checks to make sure an element exists and is in the DOM.  The
20779          * main purpose is to handle cases where innerHTML is used to remove
20780          * drag and drop objects from the DOM.  IE provides an 'unspecified
20781          * error' when trying to access the offsetParent of such an element
20782          * @method verifyEl
20783          * @param {HTMLElement} el the element to check
20784          * @return {boolean} true if the element looks usable
20785          * @static
20786          */
20787         verifyEl: function(el) {
20788             if (el) {
20789                 var parent;
20790                 if(Roo.isIE){
20791                     try{
20792                         parent = el.offsetParent;
20793                     }catch(e){}
20794                 }else{
20795                     parent = el.offsetParent;
20796                 }
20797                 if (parent) {
20798                     return true;
20799                 }
20800             }
20801
20802             return false;
20803         },
20804
20805         /**
20806          * Returns a Region object containing the drag and drop element's position
20807          * and size, including the padding configured for it
20808          * @method getLocation
20809          * @param {DragDrop} oDD the drag and drop object to get the
20810          *                       location for
20811          * @return {Roo.lib.Region} a Region object representing the total area
20812          *                             the element occupies, including any padding
20813          *                             the instance is configured for.
20814          * @static
20815          */
20816         getLocation: function(oDD) {
20817             if (! this.isTypeOfDD(oDD)) {
20818                 return null;
20819             }
20820
20821             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20822
20823             try {
20824                 pos= Roo.lib.Dom.getXY(el);
20825             } catch (e) { }
20826
20827             if (!pos) {
20828                 return null;
20829             }
20830
20831             x1 = pos[0];
20832             x2 = x1 + el.offsetWidth;
20833             y1 = pos[1];
20834             y2 = y1 + el.offsetHeight;
20835
20836             t = y1 - oDD.padding[0];
20837             r = x2 + oDD.padding[1];
20838             b = y2 + oDD.padding[2];
20839             l = x1 - oDD.padding[3];
20840
20841             return new Roo.lib.Region( t, r, b, l );
20842         },
20843
20844         /**
20845          * Checks the cursor location to see if it over the target
20846          * @method isOverTarget
20847          * @param {Roo.lib.Point} pt The point to evaluate
20848          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20849          * @return {boolean} true if the mouse is over the target
20850          * @private
20851          * @static
20852          */
20853         isOverTarget: function(pt, oTarget, intersect) {
20854             // use cache if available
20855             var loc = this.locationCache[oTarget.id];
20856             if (!loc || !this.useCache) {
20857                 loc = this.getLocation(oTarget);
20858                 this.locationCache[oTarget.id] = loc;
20859
20860             }
20861
20862             if (!loc) {
20863                 return false;
20864             }
20865
20866             oTarget.cursorIsOver = loc.contains( pt );
20867
20868             // DragDrop is using this as a sanity check for the initial mousedown
20869             // in this case we are done.  In POINT mode, if the drag obj has no
20870             // contraints, we are also done. Otherwise we need to evaluate the
20871             // location of the target as related to the actual location of the
20872             // dragged element.
20873             var dc = this.dragCurrent;
20874             if (!dc || !dc.getTargetCoord ||
20875                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20876                 return oTarget.cursorIsOver;
20877             }
20878
20879             oTarget.overlap = null;
20880
20881             // Get the current location of the drag element, this is the
20882             // location of the mouse event less the delta that represents
20883             // where the original mousedown happened on the element.  We
20884             // need to consider constraints and ticks as well.
20885             var pos = dc.getTargetCoord(pt.x, pt.y);
20886
20887             var el = dc.getDragEl();
20888             var curRegion = new Roo.lib.Region( pos.y,
20889                                                    pos.x + el.offsetWidth,
20890                                                    pos.y + el.offsetHeight,
20891                                                    pos.x );
20892
20893             var overlap = curRegion.intersect(loc);
20894
20895             if (overlap) {
20896                 oTarget.overlap = overlap;
20897                 return (intersect) ? true : oTarget.cursorIsOver;
20898             } else {
20899                 return false;
20900             }
20901         },
20902
20903         /**
20904          * unload event handler
20905          * @method _onUnload
20906          * @private
20907          * @static
20908          */
20909         _onUnload: function(e, me) {
20910             Roo.dd.DragDropMgr.unregAll();
20911         },
20912
20913         /**
20914          * Cleans up the drag and drop events and objects.
20915          * @method unregAll
20916          * @private
20917          * @static
20918          */
20919         unregAll: function() {
20920
20921             if (this.dragCurrent) {
20922                 this.stopDrag();
20923                 this.dragCurrent = null;
20924             }
20925
20926             this._execOnAll("unreg", []);
20927
20928             for (i in this.elementCache) {
20929                 delete this.elementCache[i];
20930             }
20931
20932             this.elementCache = {};
20933             this.ids = {};
20934         },
20935
20936         /**
20937          * A cache of DOM elements
20938          * @property elementCache
20939          * @private
20940          * @static
20941          */
20942         elementCache: {},
20943
20944         /**
20945          * Get the wrapper for the DOM element specified
20946          * @method getElWrapper
20947          * @param {String} id the id of the element to get
20948          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20949          * @private
20950          * @deprecated This wrapper isn't that useful
20951          * @static
20952          */
20953         getElWrapper: function(id) {
20954             var oWrapper = this.elementCache[id];
20955             if (!oWrapper || !oWrapper.el) {
20956                 oWrapper = this.elementCache[id] =
20957                     new this.ElementWrapper(Roo.getDom(id));
20958             }
20959             return oWrapper;
20960         },
20961
20962         /**
20963          * Returns the actual DOM element
20964          * @method getElement
20965          * @param {String} id the id of the elment to get
20966          * @return {Object} The element
20967          * @deprecated use Roo.getDom instead
20968          * @static
20969          */
20970         getElement: function(id) {
20971             return Roo.getDom(id);
20972         },
20973
20974         /**
20975          * Returns the style property for the DOM element (i.e.,
20976          * document.getElById(id).style)
20977          * @method getCss
20978          * @param {String} id the id of the elment to get
20979          * @return {Object} The style property of the element
20980          * @deprecated use Roo.getDom instead
20981          * @static
20982          */
20983         getCss: function(id) {
20984             var el = Roo.getDom(id);
20985             return (el) ? el.style : null;
20986         },
20987
20988         /**
20989          * Inner class for cached elements
20990          * @class DragDropMgr.ElementWrapper
20991          * @for DragDropMgr
20992          * @private
20993          * @deprecated
20994          */
20995         ElementWrapper: function(el) {
20996                 /**
20997                  * The element
20998                  * @property el
20999                  */
21000                 this.el = el || null;
21001                 /**
21002                  * The element id
21003                  * @property id
21004                  */
21005                 this.id = this.el && el.id;
21006                 /**
21007                  * A reference to the style property
21008                  * @property css
21009                  */
21010                 this.css = this.el && el.style;
21011             },
21012
21013         /**
21014          * Returns the X position of an html element
21015          * @method getPosX
21016          * @param el the element for which to get the position
21017          * @return {int} the X coordinate
21018          * @for DragDropMgr
21019          * @deprecated use Roo.lib.Dom.getX instead
21020          * @static
21021          */
21022         getPosX: function(el) {
21023             return Roo.lib.Dom.getX(el);
21024         },
21025
21026         /**
21027          * Returns the Y position of an html element
21028          * @method getPosY
21029          * @param el the element for which to get the position
21030          * @return {int} the Y coordinate
21031          * @deprecated use Roo.lib.Dom.getY instead
21032          * @static
21033          */
21034         getPosY: function(el) {
21035             return Roo.lib.Dom.getY(el);
21036         },
21037
21038         /**
21039          * Swap two nodes.  In IE, we use the native method, for others we
21040          * emulate the IE behavior
21041          * @method swapNode
21042          * @param n1 the first node to swap
21043          * @param n2 the other node to swap
21044          * @static
21045          */
21046         swapNode: function(n1, n2) {
21047             if (n1.swapNode) {
21048                 n1.swapNode(n2);
21049             } else {
21050                 var p = n2.parentNode;
21051                 var s = n2.nextSibling;
21052
21053                 if (s == n1) {
21054                     p.insertBefore(n1, n2);
21055                 } else if (n2 == n1.nextSibling) {
21056                     p.insertBefore(n2, n1);
21057                 } else {
21058                     n1.parentNode.replaceChild(n2, n1);
21059                     p.insertBefore(n1, s);
21060                 }
21061             }
21062         },
21063
21064         /**
21065          * Returns the current scroll position
21066          * @method getScroll
21067          * @private
21068          * @static
21069          */
21070         getScroll: function () {
21071             var t, l, dde=document.documentElement, db=document.body;
21072             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21073                 t = dde.scrollTop;
21074                 l = dde.scrollLeft;
21075             } else if (db) {
21076                 t = db.scrollTop;
21077                 l = db.scrollLeft;
21078             } else {
21079
21080             }
21081             return { top: t, left: l };
21082         },
21083
21084         /**
21085          * Returns the specified element style property
21086          * @method getStyle
21087          * @param {HTMLElement} el          the element
21088          * @param {string}      styleProp   the style property
21089          * @return {string} The value of the style property
21090          * @deprecated use Roo.lib.Dom.getStyle
21091          * @static
21092          */
21093         getStyle: function(el, styleProp) {
21094             return Roo.fly(el).getStyle(styleProp);
21095         },
21096
21097         /**
21098          * Gets the scrollTop
21099          * @method getScrollTop
21100          * @return {int} the document's scrollTop
21101          * @static
21102          */
21103         getScrollTop: function () { return this.getScroll().top; },
21104
21105         /**
21106          * Gets the scrollLeft
21107          * @method getScrollLeft
21108          * @return {int} the document's scrollTop
21109          * @static
21110          */
21111         getScrollLeft: function () { return this.getScroll().left; },
21112
21113         /**
21114          * Sets the x/y position of an element to the location of the
21115          * target element.
21116          * @method moveToEl
21117          * @param {HTMLElement} moveEl      The element to move
21118          * @param {HTMLElement} targetEl    The position reference element
21119          * @static
21120          */
21121         moveToEl: function (moveEl, targetEl) {
21122             var aCoord = Roo.lib.Dom.getXY(targetEl);
21123             Roo.lib.Dom.setXY(moveEl, aCoord);
21124         },
21125
21126         /**
21127          * Numeric array sort function
21128          * @method numericSort
21129          * @static
21130          */
21131         numericSort: function(a, b) { return (a - b); },
21132
21133         /**
21134          * Internal counter
21135          * @property _timeoutCount
21136          * @private
21137          * @static
21138          */
21139         _timeoutCount: 0,
21140
21141         /**
21142          * Trying to make the load order less important.  Without this we get
21143          * an error if this file is loaded before the Event Utility.
21144          * @method _addListeners
21145          * @private
21146          * @static
21147          */
21148         _addListeners: function() {
21149             var DDM = Roo.dd.DDM;
21150             if ( Roo.lib.Event && document ) {
21151                 DDM._onLoad();
21152             } else {
21153                 if (DDM._timeoutCount > 2000) {
21154                 } else {
21155                     setTimeout(DDM._addListeners, 10);
21156                     if (document && document.body) {
21157                         DDM._timeoutCount += 1;
21158                     }
21159                 }
21160             }
21161         },
21162
21163         /**
21164          * Recursively searches the immediate parent and all child nodes for
21165          * the handle element in order to determine wheter or not it was
21166          * clicked.
21167          * @method handleWasClicked
21168          * @param node the html element to inspect
21169          * @static
21170          */
21171         handleWasClicked: function(node, id) {
21172             if (this.isHandle(id, node.id)) {
21173                 return true;
21174             } else {
21175                 // check to see if this is a text node child of the one we want
21176                 var p = node.parentNode;
21177
21178                 while (p) {
21179                     if (this.isHandle(id, p.id)) {
21180                         return true;
21181                     } else {
21182                         p = p.parentNode;
21183                     }
21184                 }
21185             }
21186
21187             return false;
21188         }
21189
21190     };
21191
21192 }();
21193
21194 // shorter alias, save a few bytes
21195 Roo.dd.DDM = Roo.dd.DragDropMgr;
21196 Roo.dd.DDM._addListeners();
21197
21198 }/*
21199  * Based on:
21200  * Ext JS Library 1.1.1
21201  * Copyright(c) 2006-2007, Ext JS, LLC.
21202  *
21203  * Originally Released Under LGPL - original licence link has changed is not relivant.
21204  *
21205  * Fork - LGPL
21206  * <script type="text/javascript">
21207  */
21208
21209 /**
21210  * @class Roo.dd.DD
21211  * A DragDrop implementation where the linked element follows the
21212  * mouse cursor during a drag.
21213  * @extends Roo.dd.DragDrop
21214  * @constructor
21215  * @param {String} id the id of the linked element
21216  * @param {String} sGroup the group of related DragDrop items
21217  * @param {object} config an object containing configurable attributes
21218  *                Valid properties for DD:
21219  *                    scroll
21220  */
21221 Roo.dd.DD = function(id, sGroup, config) {
21222     if (id) {
21223         this.init(id, sGroup, config);
21224     }
21225 };
21226
21227 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
21228
21229     /**
21230      * When set to true, the utility automatically tries to scroll the browser
21231      * window wehn a drag and drop element is dragged near the viewport boundary.
21232      * Defaults to true.
21233      * @property scroll
21234      * @type boolean
21235      */
21236     scroll: true,
21237
21238     /**
21239      * Sets the pointer offset to the distance between the linked element's top
21240      * left corner and the location the element was clicked
21241      * @method autoOffset
21242      * @param {int} iPageX the X coordinate of the click
21243      * @param {int} iPageY the Y coordinate of the click
21244      */
21245     autoOffset: function(iPageX, iPageY) {
21246         var x = iPageX - this.startPageX;
21247         var y = iPageY - this.startPageY;
21248         this.setDelta(x, y);
21249     },
21250
21251     /**
21252      * Sets the pointer offset.  You can call this directly to force the
21253      * offset to be in a particular location (e.g., pass in 0,0 to set it
21254      * to the center of the object)
21255      * @method setDelta
21256      * @param {int} iDeltaX the distance from the left
21257      * @param {int} iDeltaY the distance from the top
21258      */
21259     setDelta: function(iDeltaX, iDeltaY) {
21260         this.deltaX = iDeltaX;
21261         this.deltaY = iDeltaY;
21262     },
21263
21264     /**
21265      * Sets the drag element to the location of the mousedown or click event,
21266      * maintaining the cursor location relative to the location on the element
21267      * that was clicked.  Override this if you want to place the element in a
21268      * location other than where the cursor is.
21269      * @method setDragElPos
21270      * @param {int} iPageX the X coordinate of the mousedown or drag event
21271      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21272      */
21273     setDragElPos: function(iPageX, iPageY) {
21274         // the first time we do this, we are going to check to make sure
21275         // the element has css positioning
21276
21277         var el = this.getDragEl();
21278         this.alignElWithMouse(el, iPageX, iPageY);
21279     },
21280
21281     /**
21282      * Sets the element to the location of the mousedown or click event,
21283      * maintaining the cursor location relative to the location on the element
21284      * that was clicked.  Override this if you want to place the element in a
21285      * location other than where the cursor is.
21286      * @method alignElWithMouse
21287      * @param {HTMLElement} el the element to move
21288      * @param {int} iPageX the X coordinate of the mousedown or drag event
21289      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21290      */
21291     alignElWithMouse: function(el, iPageX, iPageY) {
21292         var oCoord = this.getTargetCoord(iPageX, iPageY);
21293         var fly = el.dom ? el : Roo.fly(el);
21294         if (!this.deltaSetXY) {
21295             var aCoord = [oCoord.x, oCoord.y];
21296             fly.setXY(aCoord);
21297             var newLeft = fly.getLeft(true);
21298             var newTop  = fly.getTop(true);
21299             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
21300         } else {
21301             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
21302         }
21303
21304         this.cachePosition(oCoord.x, oCoord.y);
21305         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
21306         return oCoord;
21307     },
21308
21309     /**
21310      * Saves the most recent position so that we can reset the constraints and
21311      * tick marks on-demand.  We need to know this so that we can calculate the
21312      * number of pixels the element is offset from its original position.
21313      * @method cachePosition
21314      * @param iPageX the current x position (optional, this just makes it so we
21315      * don't have to look it up again)
21316      * @param iPageY the current y position (optional, this just makes it so we
21317      * don't have to look it up again)
21318      */
21319     cachePosition: function(iPageX, iPageY) {
21320         if (iPageX) {
21321             this.lastPageX = iPageX;
21322             this.lastPageY = iPageY;
21323         } else {
21324             var aCoord = Roo.lib.Dom.getXY(this.getEl());
21325             this.lastPageX = aCoord[0];
21326             this.lastPageY = aCoord[1];
21327         }
21328     },
21329
21330     /**
21331      * Auto-scroll the window if the dragged object has been moved beyond the
21332      * visible window boundary.
21333      * @method autoScroll
21334      * @param {int} x the drag element's x position
21335      * @param {int} y the drag element's y position
21336      * @param {int} h the height of the drag element
21337      * @param {int} w the width of the drag element
21338      * @private
21339      */
21340     autoScroll: function(x, y, h, w) {
21341
21342         if (this.scroll) {
21343             // The client height
21344             var clientH = Roo.lib.Dom.getViewWidth();
21345
21346             // The client width
21347             var clientW = Roo.lib.Dom.getViewHeight();
21348
21349             // The amt scrolled down
21350             var st = this.DDM.getScrollTop();
21351
21352             // The amt scrolled right
21353             var sl = this.DDM.getScrollLeft();
21354
21355             // Location of the bottom of the element
21356             var bot = h + y;
21357
21358             // Location of the right of the element
21359             var right = w + x;
21360
21361             // The distance from the cursor to the bottom of the visible area,
21362             // adjusted so that we don't scroll if the cursor is beyond the
21363             // element drag constraints
21364             var toBot = (clientH + st - y - this.deltaY);
21365
21366             // The distance from the cursor to the right of the visible area
21367             var toRight = (clientW + sl - x - this.deltaX);
21368
21369
21370             // How close to the edge the cursor must be before we scroll
21371             // var thresh = (document.all) ? 100 : 40;
21372             var thresh = 40;
21373
21374             // How many pixels to scroll per autoscroll op.  This helps to reduce
21375             // clunky scrolling. IE is more sensitive about this ... it needs this
21376             // value to be higher.
21377             var scrAmt = (document.all) ? 80 : 30;
21378
21379             // Scroll down if we are near the bottom of the visible page and the
21380             // obj extends below the crease
21381             if ( bot > clientH && toBot < thresh ) {
21382                 window.scrollTo(sl, st + scrAmt);
21383             }
21384
21385             // Scroll up if the window is scrolled down and the top of the object
21386             // goes above the top border
21387             if ( y < st && st > 0 && y - st < thresh ) {
21388                 window.scrollTo(sl, st - scrAmt);
21389             }
21390
21391             // Scroll right if the obj is beyond the right border and the cursor is
21392             // near the border.
21393             if ( right > clientW && toRight < thresh ) {
21394                 window.scrollTo(sl + scrAmt, st);
21395             }
21396
21397             // Scroll left if the window has been scrolled to the right and the obj
21398             // extends past the left border
21399             if ( x < sl && sl > 0 && x - sl < thresh ) {
21400                 window.scrollTo(sl - scrAmt, st);
21401             }
21402         }
21403     },
21404
21405     /**
21406      * Finds the location the element should be placed if we want to move
21407      * it to where the mouse location less the click offset would place us.
21408      * @method getTargetCoord
21409      * @param {int} iPageX the X coordinate of the click
21410      * @param {int} iPageY the Y coordinate of the click
21411      * @return an object that contains the coordinates (Object.x and Object.y)
21412      * @private
21413      */
21414     getTargetCoord: function(iPageX, iPageY) {
21415
21416
21417         var x = iPageX - this.deltaX;
21418         var y = iPageY - this.deltaY;
21419
21420         if (this.constrainX) {
21421             if (x < this.minX) { x = this.minX; }
21422             if (x > this.maxX) { x = this.maxX; }
21423         }
21424
21425         if (this.constrainY) {
21426             if (y < this.minY) { y = this.minY; }
21427             if (y > this.maxY) { y = this.maxY; }
21428         }
21429
21430         x = this.getTick(x, this.xTicks);
21431         y = this.getTick(y, this.yTicks);
21432
21433
21434         return {x:x, y:y};
21435     },
21436
21437     /*
21438      * Sets up config options specific to this class. Overrides
21439      * Roo.dd.DragDrop, but all versions of this method through the
21440      * inheritance chain are called
21441      */
21442     applyConfig: function() {
21443         Roo.dd.DD.superclass.applyConfig.call(this);
21444         this.scroll = (this.config.scroll !== false);
21445     },
21446
21447     /*
21448      * Event that fires prior to the onMouseDown event.  Overrides
21449      * Roo.dd.DragDrop.
21450      */
21451     b4MouseDown: function(e) {
21452         // this.resetConstraints();
21453         this.autoOffset(e.getPageX(),
21454                             e.getPageY());
21455     },
21456
21457     /*
21458      * Event that fires prior to the onDrag event.  Overrides
21459      * Roo.dd.DragDrop.
21460      */
21461     b4Drag: function(e) {
21462         this.setDragElPos(e.getPageX(),
21463                             e.getPageY());
21464     },
21465
21466     toString: function() {
21467         return ("DD " + this.id);
21468     }
21469
21470     //////////////////////////////////////////////////////////////////////////
21471     // Debugging ygDragDrop events that can be overridden
21472     //////////////////////////////////////////////////////////////////////////
21473     /*
21474     startDrag: function(x, y) {
21475     },
21476
21477     onDrag: function(e) {
21478     },
21479
21480     onDragEnter: function(e, id) {
21481     },
21482
21483     onDragOver: function(e, id) {
21484     },
21485
21486     onDragOut: function(e, id) {
21487     },
21488
21489     onDragDrop: function(e, id) {
21490     },
21491
21492     endDrag: function(e) {
21493     }
21494
21495     */
21496
21497 });/*
21498  * Based on:
21499  * Ext JS Library 1.1.1
21500  * Copyright(c) 2006-2007, Ext JS, LLC.
21501  *
21502  * Originally Released Under LGPL - original licence link has changed is not relivant.
21503  *
21504  * Fork - LGPL
21505  * <script type="text/javascript">
21506  */
21507
21508 /**
21509  * @class Roo.dd.DDProxy
21510  * A DragDrop implementation that inserts an empty, bordered div into
21511  * the document that follows the cursor during drag operations.  At the time of
21512  * the click, the frame div is resized to the dimensions of the linked html
21513  * element, and moved to the exact location of the linked element.
21514  *
21515  * References to the "frame" element refer to the single proxy element that
21516  * was created to be dragged in place of all DDProxy elements on the
21517  * page.
21518  *
21519  * @extends Roo.dd.DD
21520  * @constructor
21521  * @param {String} id the id of the linked html element
21522  * @param {String} sGroup the group of related DragDrop objects
21523  * @param {object} config an object containing configurable attributes
21524  *                Valid properties for DDProxy in addition to those in DragDrop:
21525  *                   resizeFrame, centerFrame, dragElId
21526  */
21527 Roo.dd.DDProxy = function(id, sGroup, config) {
21528     if (id) {
21529         this.init(id, sGroup, config);
21530         this.initFrame();
21531     }
21532 };
21533
21534 /**
21535  * The default drag frame div id
21536  * @property Roo.dd.DDProxy.dragElId
21537  * @type String
21538  * @static
21539  */
21540 Roo.dd.DDProxy.dragElId = "ygddfdiv";
21541
21542 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
21543
21544     /**
21545      * By default we resize the drag frame to be the same size as the element
21546      * we want to drag (this is to get the frame effect).  We can turn it off
21547      * if we want a different behavior.
21548      * @property resizeFrame
21549      * @type boolean
21550      */
21551     resizeFrame: true,
21552
21553     /**
21554      * By default the frame is positioned exactly where the drag element is, so
21555      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
21556      * you do not have constraints on the obj is to have the drag frame centered
21557      * around the cursor.  Set centerFrame to true for this effect.
21558      * @property centerFrame
21559      * @type boolean
21560      */
21561     centerFrame: false,
21562
21563     /**
21564      * Creates the proxy element if it does not yet exist
21565      * @method createFrame
21566      */
21567     createFrame: function() {
21568         var self = this;
21569         var body = document.body;
21570
21571         if (!body || !body.firstChild) {
21572             setTimeout( function() { self.createFrame(); }, 50 );
21573             return;
21574         }
21575
21576         var div = this.getDragEl();
21577
21578         if (!div) {
21579             div    = document.createElement("div");
21580             div.id = this.dragElId;
21581             var s  = div.style;
21582
21583             s.position   = "absolute";
21584             s.visibility = "hidden";
21585             s.cursor     = "move";
21586             s.border     = "2px solid #aaa";
21587             s.zIndex     = 999;
21588
21589             // appendChild can blow up IE if invoked prior to the window load event
21590             // while rendering a table.  It is possible there are other scenarios
21591             // that would cause this to happen as well.
21592             body.insertBefore(div, body.firstChild);
21593         }
21594     },
21595
21596     /**
21597      * Initialization for the drag frame element.  Must be called in the
21598      * constructor of all subclasses
21599      * @method initFrame
21600      */
21601     initFrame: function() {
21602         this.createFrame();
21603     },
21604
21605     applyConfig: function() {
21606         Roo.dd.DDProxy.superclass.applyConfig.call(this);
21607
21608         this.resizeFrame = (this.config.resizeFrame !== false);
21609         this.centerFrame = (this.config.centerFrame);
21610         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
21611     },
21612
21613     /**
21614      * Resizes the drag frame to the dimensions of the clicked object, positions
21615      * it over the object, and finally displays it
21616      * @method showFrame
21617      * @param {int} iPageX X click position
21618      * @param {int} iPageY Y click position
21619      * @private
21620      */
21621     showFrame: function(iPageX, iPageY) {
21622         var el = this.getEl();
21623         var dragEl = this.getDragEl();
21624         var s = dragEl.style;
21625
21626         this._resizeProxy();
21627
21628         if (this.centerFrame) {
21629             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21630                            Math.round(parseInt(s.height, 10)/2) );
21631         }
21632
21633         this.setDragElPos(iPageX, iPageY);
21634
21635         Roo.fly(dragEl).show();
21636     },
21637
21638     /**
21639      * The proxy is automatically resized to the dimensions of the linked
21640      * element when a drag is initiated, unless resizeFrame is set to false
21641      * @method _resizeProxy
21642      * @private
21643      */
21644     _resizeProxy: function() {
21645         if (this.resizeFrame) {
21646             var el = this.getEl();
21647             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21648         }
21649     },
21650
21651     // overrides Roo.dd.DragDrop
21652     b4MouseDown: function(e) {
21653         var x = e.getPageX();
21654         var y = e.getPageY();
21655         this.autoOffset(x, y);
21656         this.setDragElPos(x, y);
21657     },
21658
21659     // overrides Roo.dd.DragDrop
21660     b4StartDrag: function(x, y) {
21661         // show the drag frame
21662         this.showFrame(x, y);
21663     },
21664
21665     // overrides Roo.dd.DragDrop
21666     b4EndDrag: function(e) {
21667         Roo.fly(this.getDragEl()).hide();
21668     },
21669
21670     // overrides Roo.dd.DragDrop
21671     // By default we try to move the element to the last location of the frame.
21672     // This is so that the default behavior mirrors that of Roo.dd.DD.
21673     endDrag: function(e) {
21674
21675         var lel = this.getEl();
21676         var del = this.getDragEl();
21677
21678         // Show the drag frame briefly so we can get its position
21679         del.style.visibility = "";
21680
21681         this.beforeMove();
21682         // Hide the linked element before the move to get around a Safari
21683         // rendering bug.
21684         lel.style.visibility = "hidden";
21685         Roo.dd.DDM.moveToEl(lel, del);
21686         del.style.visibility = "hidden";
21687         lel.style.visibility = "";
21688
21689         this.afterDrag();
21690     },
21691
21692     beforeMove : function(){
21693
21694     },
21695
21696     afterDrag : function(){
21697
21698     },
21699
21700     toString: function() {
21701         return ("DDProxy " + this.id);
21702     }
21703
21704 });
21705 /*
21706  * Based on:
21707  * Ext JS Library 1.1.1
21708  * Copyright(c) 2006-2007, Ext JS, LLC.
21709  *
21710  * Originally Released Under LGPL - original licence link has changed is not relivant.
21711  *
21712  * Fork - LGPL
21713  * <script type="text/javascript">
21714  */
21715
21716  /**
21717  * @class Roo.dd.DDTarget
21718  * A DragDrop implementation that does not move, but can be a drop
21719  * target.  You would get the same result by simply omitting implementation
21720  * for the event callbacks, but this way we reduce the processing cost of the
21721  * event listener and the callbacks.
21722  * @extends Roo.dd.DragDrop
21723  * @constructor
21724  * @param {String} id the id of the element that is a drop target
21725  * @param {String} sGroup the group of related DragDrop objects
21726  * @param {object} config an object containing configurable attributes
21727  *                 Valid properties for DDTarget in addition to those in
21728  *                 DragDrop:
21729  *                    none
21730  */
21731 Roo.dd.DDTarget = function(id, sGroup, config) {
21732     if (id) {
21733         this.initTarget(id, sGroup, config);
21734     }
21735     if (config && (config.listeners || config.events)) { 
21736         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21737             listeners : config.listeners || {}, 
21738             events : config.events || {} 
21739         });    
21740     }
21741 };
21742
21743 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21744 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21745     toString: function() {
21746         return ("DDTarget " + this.id);
21747     }
21748 });
21749 /*
21750  * Based on:
21751  * Ext JS Library 1.1.1
21752  * Copyright(c) 2006-2007, Ext JS, LLC.
21753  *
21754  * Originally Released Under LGPL - original licence link has changed is not relivant.
21755  *
21756  * Fork - LGPL
21757  * <script type="text/javascript">
21758  */
21759  
21760
21761 /**
21762  * @class Roo.dd.ScrollManager
21763  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21764  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21765  * @singleton
21766  */
21767 Roo.dd.ScrollManager = function(){
21768     var ddm = Roo.dd.DragDropMgr;
21769     var els = {};
21770     var dragEl = null;
21771     var proc = {};
21772     
21773     
21774     
21775     var onStop = function(e){
21776         dragEl = null;
21777         clearProc();
21778     };
21779     
21780     var triggerRefresh = function(){
21781         if(ddm.dragCurrent){
21782              ddm.refreshCache(ddm.dragCurrent.groups);
21783         }
21784     };
21785     
21786     var doScroll = function(){
21787         if(ddm.dragCurrent){
21788             var dds = Roo.dd.ScrollManager;
21789             if(!dds.animate){
21790                 if(proc.el.scroll(proc.dir, dds.increment)){
21791                     triggerRefresh();
21792                 }
21793             }else{
21794                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21795             }
21796         }
21797     };
21798     
21799     var clearProc = function(){
21800         if(proc.id){
21801             clearInterval(proc.id);
21802         }
21803         proc.id = 0;
21804         proc.el = null;
21805         proc.dir = "";
21806     };
21807     
21808     var startProc = function(el, dir){
21809          Roo.log('scroll startproc');
21810         clearProc();
21811         proc.el = el;
21812         proc.dir = dir;
21813         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21814     };
21815     
21816     var onFire = function(e, isDrop){
21817        
21818         if(isDrop || !ddm.dragCurrent){ return; }
21819         var dds = Roo.dd.ScrollManager;
21820         if(!dragEl || dragEl != ddm.dragCurrent){
21821             dragEl = ddm.dragCurrent;
21822             // refresh regions on drag start
21823             dds.refreshCache();
21824         }
21825         
21826         var xy = Roo.lib.Event.getXY(e);
21827         var pt = new Roo.lib.Point(xy[0], xy[1]);
21828         for(var id in els){
21829             var el = els[id], r = el._region;
21830             if(r && r.contains(pt) && el.isScrollable()){
21831                 if(r.bottom - pt.y <= dds.thresh){
21832                     if(proc.el != el){
21833                         startProc(el, "down");
21834                     }
21835                     return;
21836                 }else if(r.right - pt.x <= dds.thresh){
21837                     if(proc.el != el){
21838                         startProc(el, "left");
21839                     }
21840                     return;
21841                 }else if(pt.y - r.top <= dds.thresh){
21842                     if(proc.el != el){
21843                         startProc(el, "up");
21844                     }
21845                     return;
21846                 }else if(pt.x - r.left <= dds.thresh){
21847                     if(proc.el != el){
21848                         startProc(el, "right");
21849                     }
21850                     return;
21851                 }
21852             }
21853         }
21854         clearProc();
21855     };
21856     
21857     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21858     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21859     
21860     return {
21861         /**
21862          * Registers new overflow element(s) to auto scroll
21863          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21864          */
21865         register : function(el){
21866             if(el instanceof Array){
21867                 for(var i = 0, len = el.length; i < len; i++) {
21868                         this.register(el[i]);
21869                 }
21870             }else{
21871                 el = Roo.get(el);
21872                 els[el.id] = el;
21873             }
21874             Roo.dd.ScrollManager.els = els;
21875         },
21876         
21877         /**
21878          * Unregisters overflow element(s) so they are no longer scrolled
21879          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21880          */
21881         unregister : function(el){
21882             if(el instanceof Array){
21883                 for(var i = 0, len = el.length; i < len; i++) {
21884                         this.unregister(el[i]);
21885                 }
21886             }else{
21887                 el = Roo.get(el);
21888                 delete els[el.id];
21889             }
21890         },
21891         
21892         /**
21893          * The number of pixels from the edge of a container the pointer needs to be to 
21894          * trigger scrolling (defaults to 25)
21895          * @type Number
21896          */
21897         thresh : 25,
21898         
21899         /**
21900          * The number of pixels to scroll in each scroll increment (defaults to 50)
21901          * @type Number
21902          */
21903         increment : 100,
21904         
21905         /**
21906          * The frequency of scrolls in milliseconds (defaults to 500)
21907          * @type Number
21908          */
21909         frequency : 500,
21910         
21911         /**
21912          * True to animate the scroll (defaults to true)
21913          * @type Boolean
21914          */
21915         animate: true,
21916         
21917         /**
21918          * The animation duration in seconds - 
21919          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21920          * @type Number
21921          */
21922         animDuration: .4,
21923         
21924         /**
21925          * Manually trigger a cache refresh.
21926          */
21927         refreshCache : function(){
21928             for(var id in els){
21929                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21930                     els[id]._region = els[id].getRegion();
21931                 }
21932             }
21933         }
21934     };
21935 }();/*
21936  * Based on:
21937  * Ext JS Library 1.1.1
21938  * Copyright(c) 2006-2007, Ext JS, LLC.
21939  *
21940  * Originally Released Under LGPL - original licence link has changed is not relivant.
21941  *
21942  * Fork - LGPL
21943  * <script type="text/javascript">
21944  */
21945  
21946
21947 /**
21948  * @class Roo.dd.Registry
21949  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21950  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21951  * @singleton
21952  */
21953 Roo.dd.Registry = function(){
21954     var elements = {}; 
21955     var handles = {}; 
21956     var autoIdSeed = 0;
21957
21958     var getId = function(el, autogen){
21959         if(typeof el == "string"){
21960             return el;
21961         }
21962         var id = el.id;
21963         if(!id && autogen !== false){
21964             id = "roodd-" + (++autoIdSeed);
21965             el.id = id;
21966         }
21967         return id;
21968     };
21969     
21970     return {
21971     /**
21972      * Register a drag drop element
21973      * @param {String|HTMLElement} element The id or DOM node to register
21974      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21975      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21976      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21977      * populated in the data object (if applicable):
21978      * <pre>
21979 Value      Description<br />
21980 ---------  ------------------------------------------<br />
21981 handles    Array of DOM nodes that trigger dragging<br />
21982            for the element being registered<br />
21983 isHandle   True if the element passed in triggers<br />
21984            dragging itself, else false
21985 </pre>
21986      */
21987         register : function(el, data){
21988             data = data || {};
21989             if(typeof el == "string"){
21990                 el = document.getElementById(el);
21991             }
21992             data.ddel = el;
21993             elements[getId(el)] = data;
21994             if(data.isHandle !== false){
21995                 handles[data.ddel.id] = data;
21996             }
21997             if(data.handles){
21998                 var hs = data.handles;
21999                 for(var i = 0, len = hs.length; i < len; i++){
22000                         handles[getId(hs[i])] = data;
22001                 }
22002             }
22003         },
22004
22005     /**
22006      * Unregister a drag drop element
22007      * @param {String|HTMLElement}  element The id or DOM node to unregister
22008      */
22009         unregister : function(el){
22010             var id = getId(el, false);
22011             var data = elements[id];
22012             if(data){
22013                 delete elements[id];
22014                 if(data.handles){
22015                     var hs = data.handles;
22016                     for(var i = 0, len = hs.length; i < len; i++){
22017                         delete handles[getId(hs[i], false)];
22018                     }
22019                 }
22020             }
22021         },
22022
22023     /**
22024      * Returns the handle registered for a DOM Node by id
22025      * @param {String|HTMLElement} id The DOM node or id to look up
22026      * @return {Object} handle The custom handle data
22027      */
22028         getHandle : function(id){
22029             if(typeof id != "string"){ // must be element?
22030                 id = id.id;
22031             }
22032             return handles[id];
22033         },
22034
22035     /**
22036      * Returns the handle that is registered for the DOM node that is the target of the event
22037      * @param {Event} e The event
22038      * @return {Object} handle The custom handle data
22039      */
22040         getHandleFromEvent : function(e){
22041             var t = Roo.lib.Event.getTarget(e);
22042             return t ? handles[t.id] : null;
22043         },
22044
22045     /**
22046      * Returns a custom data object that is registered for a DOM node by id
22047      * @param {String|HTMLElement} id The DOM node or id to look up
22048      * @return {Object} data The custom data
22049      */
22050         getTarget : function(id){
22051             if(typeof id != "string"){ // must be element?
22052                 id = id.id;
22053             }
22054             return elements[id];
22055         },
22056
22057     /**
22058      * Returns a custom data object that is registered for the DOM node that is the target of the event
22059      * @param {Event} e The event
22060      * @return {Object} data The custom data
22061      */
22062         getTargetFromEvent : function(e){
22063             var t = Roo.lib.Event.getTarget(e);
22064             return t ? elements[t.id] || handles[t.id] : null;
22065         }
22066     };
22067 }();/*
22068  * Based on:
22069  * Ext JS Library 1.1.1
22070  * Copyright(c) 2006-2007, Ext JS, LLC.
22071  *
22072  * Originally Released Under LGPL - original licence link has changed is not relivant.
22073  *
22074  * Fork - LGPL
22075  * <script type="text/javascript">
22076  */
22077  
22078
22079 /**
22080  * @class Roo.dd.StatusProxy
22081  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22082  * default drag proxy used by all Roo.dd components.
22083  * @constructor
22084  * @param {Object} config
22085  */
22086 Roo.dd.StatusProxy = function(config){
22087     Roo.apply(this, config);
22088     this.id = this.id || Roo.id();
22089     this.el = new Roo.Layer({
22090         dh: {
22091             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22092                 {tag: "div", cls: "x-dd-drop-icon"},
22093                 {tag: "div", cls: "x-dd-drag-ghost"}
22094             ]
22095         }, 
22096         shadow: !config || config.shadow !== false
22097     });
22098     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22099     this.dropStatus = this.dropNotAllowed;
22100 };
22101
22102 Roo.dd.StatusProxy.prototype = {
22103     /**
22104      * @cfg {String} dropAllowed
22105      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22106      */
22107     dropAllowed : "x-dd-drop-ok",
22108     /**
22109      * @cfg {String} dropNotAllowed
22110      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22111      */
22112     dropNotAllowed : "x-dd-drop-nodrop",
22113
22114     /**
22115      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22116      * over the current target element.
22117      * @param {String} cssClass The css class for the new drop status indicator image
22118      */
22119     setStatus : function(cssClass){
22120         cssClass = cssClass || this.dropNotAllowed;
22121         if(this.dropStatus != cssClass){
22122             this.el.replaceClass(this.dropStatus, cssClass);
22123             this.dropStatus = cssClass;
22124         }
22125     },
22126
22127     /**
22128      * Resets the status indicator to the default dropNotAllowed value
22129      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
22130      */
22131     reset : function(clearGhost){
22132         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
22133         this.dropStatus = this.dropNotAllowed;
22134         if(clearGhost){
22135             this.ghost.update("");
22136         }
22137     },
22138
22139     /**
22140      * Updates the contents of the ghost element
22141      * @param {String} html The html that will replace the current innerHTML of the ghost element
22142      */
22143     update : function(html){
22144         if(typeof html == "string"){
22145             this.ghost.update(html);
22146         }else{
22147             this.ghost.update("");
22148             html.style.margin = "0";
22149             this.ghost.dom.appendChild(html);
22150         }
22151         // ensure float = none set?? cant remember why though.
22152         var el = this.ghost.dom.firstChild;
22153                 if(el){
22154                         Roo.fly(el).setStyle('float', 'none');
22155                 }
22156     },
22157     
22158     /**
22159      * Returns the underlying proxy {@link Roo.Layer}
22160      * @return {Roo.Layer} el
22161     */
22162     getEl : function(){
22163         return this.el;
22164     },
22165
22166     /**
22167      * Returns the ghost element
22168      * @return {Roo.Element} el
22169      */
22170     getGhost : function(){
22171         return this.ghost;
22172     },
22173
22174     /**
22175      * Hides the proxy
22176      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
22177      */
22178     hide : function(clear){
22179         this.el.hide();
22180         if(clear){
22181             this.reset(true);
22182         }
22183     },
22184
22185     /**
22186      * Stops the repair animation if it's currently running
22187      */
22188     stop : function(){
22189         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
22190             this.anim.stop();
22191         }
22192     },
22193
22194     /**
22195      * Displays this proxy
22196      */
22197     show : function(){
22198         this.el.show();
22199     },
22200
22201     /**
22202      * Force the Layer to sync its shadow and shim positions to the element
22203      */
22204     sync : function(){
22205         this.el.sync();
22206     },
22207
22208     /**
22209      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
22210      * invalid drop operation by the item being dragged.
22211      * @param {Array} xy The XY position of the element ([x, y])
22212      * @param {Function} callback The function to call after the repair is complete
22213      * @param {Object} scope The scope in which to execute the callback
22214      */
22215     repair : function(xy, callback, scope){
22216         this.callback = callback;
22217         this.scope = scope;
22218         if(xy && this.animRepair !== false){
22219             this.el.addClass("x-dd-drag-repair");
22220             this.el.hideUnders(true);
22221             this.anim = this.el.shift({
22222                 duration: this.repairDuration || .5,
22223                 easing: 'easeOut',
22224                 xy: xy,
22225                 stopFx: true,
22226                 callback: this.afterRepair,
22227                 scope: this
22228             });
22229         }else{
22230             this.afterRepair();
22231         }
22232     },
22233
22234     // private
22235     afterRepair : function(){
22236         this.hide(true);
22237         if(typeof this.callback == "function"){
22238             this.callback.call(this.scope || this);
22239         }
22240         this.callback = null;
22241         this.scope = null;
22242     }
22243 };/*
22244  * Based on:
22245  * Ext JS Library 1.1.1
22246  * Copyright(c) 2006-2007, Ext JS, LLC.
22247  *
22248  * Originally Released Under LGPL - original licence link has changed is not relivant.
22249  *
22250  * Fork - LGPL
22251  * <script type="text/javascript">
22252  */
22253
22254 /**
22255  * @class Roo.dd.DragSource
22256  * @extends Roo.dd.DDProxy
22257  * A simple class that provides the basic implementation needed to make any element draggable.
22258  * @constructor
22259  * @param {String/HTMLElement/Element} el The container element
22260  * @param {Object} config
22261  */
22262 Roo.dd.DragSource = function(el, config){
22263     this.el = Roo.get(el);
22264     this.dragData = {};
22265     
22266     Roo.apply(this, config);
22267     
22268     if(!this.proxy){
22269         this.proxy = new Roo.dd.StatusProxy();
22270     }
22271
22272     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
22273           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
22274     
22275     this.dragging = false;
22276 };
22277
22278 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
22279     /**
22280      * @cfg {String} dropAllowed
22281      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22282      */
22283     dropAllowed : "x-dd-drop-ok",
22284     /**
22285      * @cfg {String} dropNotAllowed
22286      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22287      */
22288     dropNotAllowed : "x-dd-drop-nodrop",
22289
22290     /**
22291      * Returns the data object associated with this drag source
22292      * @return {Object} data An object containing arbitrary data
22293      */
22294     getDragData : function(e){
22295         return this.dragData;
22296     },
22297
22298     // private
22299     onDragEnter : function(e, id){
22300         var target = Roo.dd.DragDropMgr.getDDById(id);
22301         this.cachedTarget = target;
22302         if(this.beforeDragEnter(target, e, id) !== false){
22303             if(target.isNotifyTarget){
22304                 var status = target.notifyEnter(this, e, this.dragData);
22305                 this.proxy.setStatus(status);
22306             }else{
22307                 this.proxy.setStatus(this.dropAllowed);
22308             }
22309             
22310             if(this.afterDragEnter){
22311                 /**
22312                  * An empty function by default, but provided so that you can perform a custom action
22313                  * when the dragged item enters the drop target by providing an implementation.
22314                  * @param {Roo.dd.DragDrop} target The drop target
22315                  * @param {Event} e The event object
22316                  * @param {String} id The id of the dragged element
22317                  * @method afterDragEnter
22318                  */
22319                 this.afterDragEnter(target, e, id);
22320             }
22321         }
22322     },
22323
22324     /**
22325      * An empty function by default, but provided so that you can perform a custom action
22326      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
22327      * @param {Roo.dd.DragDrop} target The drop target
22328      * @param {Event} e The event object
22329      * @param {String} id The id of the dragged element
22330      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22331      */
22332     beforeDragEnter : function(target, e, id){
22333         return true;
22334     },
22335
22336     // private
22337     alignElWithMouse: function() {
22338         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
22339         this.proxy.sync();
22340     },
22341
22342     // private
22343     onDragOver : function(e, id){
22344         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22345         if(this.beforeDragOver(target, e, id) !== false){
22346             if(target.isNotifyTarget){
22347                 var status = target.notifyOver(this, e, this.dragData);
22348                 this.proxy.setStatus(status);
22349             }
22350
22351             if(this.afterDragOver){
22352                 /**
22353                  * An empty function by default, but provided so that you can perform a custom action
22354                  * while the dragged item is over the drop target by providing an implementation.
22355                  * @param {Roo.dd.DragDrop} target The drop target
22356                  * @param {Event} e The event object
22357                  * @param {String} id The id of the dragged element
22358                  * @method afterDragOver
22359                  */
22360                 this.afterDragOver(target, e, id);
22361             }
22362         }
22363     },
22364
22365     /**
22366      * An empty function by default, but provided so that you can perform a custom action
22367      * while the dragged item is over the drop target and optionally cancel the onDragOver.
22368      * @param {Roo.dd.DragDrop} target The drop target
22369      * @param {Event} e The event object
22370      * @param {String} id The id of the dragged element
22371      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22372      */
22373     beforeDragOver : function(target, e, id){
22374         return true;
22375     },
22376
22377     // private
22378     onDragOut : function(e, id){
22379         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22380         if(this.beforeDragOut(target, e, id) !== false){
22381             if(target.isNotifyTarget){
22382                 target.notifyOut(this, e, this.dragData);
22383             }
22384             this.proxy.reset();
22385             if(this.afterDragOut){
22386                 /**
22387                  * An empty function by default, but provided so that you can perform a custom action
22388                  * after the dragged item is dragged out of the target without dropping.
22389                  * @param {Roo.dd.DragDrop} target The drop target
22390                  * @param {Event} e The event object
22391                  * @param {String} id The id of the dragged element
22392                  * @method afterDragOut
22393                  */
22394                 this.afterDragOut(target, e, id);
22395             }
22396         }
22397         this.cachedTarget = null;
22398     },
22399
22400     /**
22401      * An empty function by default, but provided so that you can perform a custom action before the dragged
22402      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
22403      * @param {Roo.dd.DragDrop} target The drop target
22404      * @param {Event} e The event object
22405      * @param {String} id The id of the dragged element
22406      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22407      */
22408     beforeDragOut : function(target, e, id){
22409         return true;
22410     },
22411     
22412     // private
22413     onDragDrop : function(e, id){
22414         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22415         if(this.beforeDragDrop(target, e, id) !== false){
22416             if(target.isNotifyTarget){
22417                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
22418                     this.onValidDrop(target, e, id);
22419                 }else{
22420                     this.onInvalidDrop(target, e, id);
22421                 }
22422             }else{
22423                 this.onValidDrop(target, e, id);
22424             }
22425             
22426             if(this.afterDragDrop){
22427                 /**
22428                  * An empty function by default, but provided so that you can perform a custom action
22429                  * after a valid drag drop has occurred by providing an implementation.
22430                  * @param {Roo.dd.DragDrop} target The drop target
22431                  * @param {Event} e The event object
22432                  * @param {String} id The id of the dropped element
22433                  * @method afterDragDrop
22434                  */
22435                 this.afterDragDrop(target, e, id);
22436             }
22437         }
22438         delete this.cachedTarget;
22439     },
22440
22441     /**
22442      * An empty function by default, but provided so that you can perform a custom action before the dragged
22443      * item is dropped onto the target and optionally cancel the onDragDrop.
22444      * @param {Roo.dd.DragDrop} target The drop target
22445      * @param {Event} e The event object
22446      * @param {String} id The id of the dragged element
22447      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
22448      */
22449     beforeDragDrop : function(target, e, id){
22450         return true;
22451     },
22452
22453     // private
22454     onValidDrop : function(target, e, id){
22455         this.hideProxy();
22456         if(this.afterValidDrop){
22457             /**
22458              * An empty function by default, but provided so that you can perform a custom action
22459              * after a valid drop has occurred by providing an implementation.
22460              * @param {Object} target The target DD 
22461              * @param {Event} e The event object
22462              * @param {String} id The id of the dropped element
22463              * @method afterInvalidDrop
22464              */
22465             this.afterValidDrop(target, e, id);
22466         }
22467     },
22468
22469     // private
22470     getRepairXY : function(e, data){
22471         return this.el.getXY();  
22472     },
22473
22474     // private
22475     onInvalidDrop : function(target, e, id){
22476         this.beforeInvalidDrop(target, e, id);
22477         if(this.cachedTarget){
22478             if(this.cachedTarget.isNotifyTarget){
22479                 this.cachedTarget.notifyOut(this, e, this.dragData);
22480             }
22481             this.cacheTarget = null;
22482         }
22483         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
22484
22485         if(this.afterInvalidDrop){
22486             /**
22487              * An empty function by default, but provided so that you can perform a custom action
22488              * after an invalid drop has occurred by providing an implementation.
22489              * @param {Event} e The event object
22490              * @param {String} id The id of the dropped element
22491              * @method afterInvalidDrop
22492              */
22493             this.afterInvalidDrop(e, id);
22494         }
22495     },
22496
22497     // private
22498     afterRepair : function(){
22499         if(Roo.enableFx){
22500             this.el.highlight(this.hlColor || "c3daf9");
22501         }
22502         this.dragging = false;
22503     },
22504
22505     /**
22506      * An empty function by default, but provided so that you can perform a custom action after an invalid
22507      * drop has occurred.
22508      * @param {Roo.dd.DragDrop} target The drop target
22509      * @param {Event} e The event object
22510      * @param {String} id The id of the dragged element
22511      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
22512      */
22513     beforeInvalidDrop : function(target, e, id){
22514         return true;
22515     },
22516
22517     // private
22518     handleMouseDown : function(e){
22519         if(this.dragging) {
22520             return;
22521         }
22522         var data = this.getDragData(e);
22523         if(data && this.onBeforeDrag(data, e) !== false){
22524             this.dragData = data;
22525             this.proxy.stop();
22526             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
22527         } 
22528     },
22529
22530     /**
22531      * An empty function by default, but provided so that you can perform a custom action before the initial
22532      * drag event begins and optionally cancel it.
22533      * @param {Object} data An object containing arbitrary data to be shared with drop targets
22534      * @param {Event} e The event object
22535      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22536      */
22537     onBeforeDrag : function(data, e){
22538         return true;
22539     },
22540
22541     /**
22542      * An empty function by default, but provided so that you can perform a custom action once the initial
22543      * drag event has begun.  The drag cannot be canceled from this function.
22544      * @param {Number} x The x position of the click on the dragged object
22545      * @param {Number} y The y position of the click on the dragged object
22546      */
22547     onStartDrag : Roo.emptyFn,
22548
22549     // private - YUI override
22550     startDrag : function(x, y){
22551         this.proxy.reset();
22552         this.dragging = true;
22553         this.proxy.update("");
22554         this.onInitDrag(x, y);
22555         this.proxy.show();
22556     },
22557
22558     // private
22559     onInitDrag : function(x, y){
22560         var clone = this.el.dom.cloneNode(true);
22561         clone.id = Roo.id(); // prevent duplicate ids
22562         this.proxy.update(clone);
22563         this.onStartDrag(x, y);
22564         return true;
22565     },
22566
22567     /**
22568      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
22569      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
22570      */
22571     getProxy : function(){
22572         return this.proxy;  
22573     },
22574
22575     /**
22576      * Hides the drag source's {@link Roo.dd.StatusProxy}
22577      */
22578     hideProxy : function(){
22579         this.proxy.hide();  
22580         this.proxy.reset(true);
22581         this.dragging = false;
22582     },
22583
22584     // private
22585     triggerCacheRefresh : function(){
22586         Roo.dd.DDM.refreshCache(this.groups);
22587     },
22588
22589     // private - override to prevent hiding
22590     b4EndDrag: function(e) {
22591     },
22592
22593     // private - override to prevent moving
22594     endDrag : function(e){
22595         this.onEndDrag(this.dragData, e);
22596     },
22597
22598     // private
22599     onEndDrag : function(data, e){
22600     },
22601     
22602     // private - pin to cursor
22603     autoOffset : function(x, y) {
22604         this.setDelta(-12, -20);
22605     }    
22606 });/*
22607  * Based on:
22608  * Ext JS Library 1.1.1
22609  * Copyright(c) 2006-2007, Ext JS, LLC.
22610  *
22611  * Originally Released Under LGPL - original licence link has changed is not relivant.
22612  *
22613  * Fork - LGPL
22614  * <script type="text/javascript">
22615  */
22616
22617
22618 /**
22619  * @class Roo.dd.DropTarget
22620  * @extends Roo.dd.DDTarget
22621  * A simple class that provides the basic implementation needed to make any element a drop target that can have
22622  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
22623  * @constructor
22624  * @param {String/HTMLElement/Element} el The container element
22625  * @param {Object} config
22626  */
22627 Roo.dd.DropTarget = function(el, config){
22628     this.el = Roo.get(el);
22629     
22630     var listeners = false; ;
22631     if (config && config.listeners) {
22632         listeners= config.listeners;
22633         delete config.listeners;
22634     }
22635     Roo.apply(this, config);
22636     
22637     if(this.containerScroll){
22638         Roo.dd.ScrollManager.register(this.el);
22639     }
22640     this.addEvents( {
22641          /**
22642          * @scope Roo.dd.DropTarget
22643          */
22644          
22645          /**
22646          * @event enter
22647          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22648          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22649          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22650          * 
22651          * IMPORTANT : it should set  this.valid to true|false
22652          * 
22653          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22654          * @param {Event} e The event
22655          * @param {Object} data An object containing arbitrary data supplied by the drag source
22656          */
22657         "enter" : true,
22658         
22659          /**
22660          * @event over
22661          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22662          * This method will be called on every mouse movement while the drag source is over the drop target.
22663          * This default implementation simply returns the dropAllowed config value.
22664          * 
22665          * IMPORTANT : it should set  this.valid to true|false
22666          * 
22667          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22668          * @param {Event} e The event
22669          * @param {Object} data An object containing arbitrary data supplied by the drag source
22670          
22671          */
22672         "over" : true,
22673         /**
22674          * @event out
22675          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22676          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22677          * overClass (if any) from the drop element.
22678          * 
22679          * 
22680          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22681          * @param {Event} e The event
22682          * @param {Object} data An object containing arbitrary data supplied by the drag source
22683          */
22684          "out" : true,
22685          
22686         /**
22687          * @event drop
22688          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22689          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22690          * implementation that does something to process the drop event and returns true so that the drag source's
22691          * repair action does not run.
22692          * 
22693          * IMPORTANT : it should set this.success
22694          * 
22695          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22696          * @param {Event} e The event
22697          * @param {Object} data An object containing arbitrary data supplied by the drag source
22698         */
22699          "drop" : true
22700     });
22701             
22702      
22703     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22704         this.el.dom, 
22705         this.ddGroup || this.group,
22706         {
22707             isTarget: true,
22708             listeners : listeners || {} 
22709            
22710         
22711         }
22712     );
22713
22714 };
22715
22716 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22717     /**
22718      * @cfg {String} overClass
22719      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22720      */
22721      /**
22722      * @cfg {String} ddGroup
22723      * The drag drop group to handle drop events for
22724      */
22725      
22726     /**
22727      * @cfg {String} dropAllowed
22728      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22729      */
22730     dropAllowed : "x-dd-drop-ok",
22731     /**
22732      * @cfg {String} dropNotAllowed
22733      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22734      */
22735     dropNotAllowed : "x-dd-drop-nodrop",
22736     /**
22737      * @cfg {boolean} success
22738      * set this after drop listener.. 
22739      */
22740     success : false,
22741     /**
22742      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22743      * if the drop point is valid for over/enter..
22744      */
22745     valid : false,
22746     // private
22747     isTarget : true,
22748
22749     // private
22750     isNotifyTarget : true,
22751     
22752     /**
22753      * @hide
22754      */
22755     notifyEnter : function(dd, e, data)
22756     {
22757         this.valid = true;
22758         this.fireEvent('enter', dd, e, data);
22759         if(this.overClass){
22760             this.el.addClass(this.overClass);
22761         }
22762         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22763             this.valid ? this.dropAllowed : this.dropNotAllowed
22764         );
22765     },
22766
22767     /**
22768      * @hide
22769      */
22770     notifyOver : function(dd, e, data)
22771     {
22772         this.valid = true;
22773         this.fireEvent('over', dd, e, data);
22774         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22775             this.valid ? this.dropAllowed : this.dropNotAllowed
22776         );
22777     },
22778
22779     /**
22780      * @hide
22781      */
22782     notifyOut : function(dd, e, data)
22783     {
22784         this.fireEvent('out', dd, e, data);
22785         if(this.overClass){
22786             this.el.removeClass(this.overClass);
22787         }
22788     },
22789
22790     /**
22791      * @hide
22792      */
22793     notifyDrop : function(dd, e, data)
22794     {
22795         this.success = false;
22796         this.fireEvent('drop', dd, e, data);
22797         return this.success;
22798     }
22799 });/*
22800  * Based on:
22801  * Ext JS Library 1.1.1
22802  * Copyright(c) 2006-2007, Ext JS, LLC.
22803  *
22804  * Originally Released Under LGPL - original licence link has changed is not relivant.
22805  *
22806  * Fork - LGPL
22807  * <script type="text/javascript">
22808  */
22809
22810
22811 /**
22812  * @class Roo.dd.DragZone
22813  * @extends Roo.dd.DragSource
22814  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22815  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22816  * @constructor
22817  * @param {String/HTMLElement/Element} el The container element
22818  * @param {Object} config
22819  */
22820 Roo.dd.DragZone = function(el, config){
22821     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22822     if(this.containerScroll){
22823         Roo.dd.ScrollManager.register(this.el);
22824     }
22825 };
22826
22827 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22828     /**
22829      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22830      * for auto scrolling during drag operations.
22831      */
22832     /**
22833      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22834      * method after a failed drop (defaults to "c3daf9" - light blue)
22835      */
22836
22837     /**
22838      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22839      * for a valid target to drag based on the mouse down. Override this method
22840      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22841      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22842      * @param {EventObject} e The mouse down event
22843      * @return {Object} The dragData
22844      */
22845     getDragData : function(e){
22846         return Roo.dd.Registry.getHandleFromEvent(e);
22847     },
22848     
22849     /**
22850      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22851      * this.dragData.ddel
22852      * @param {Number} x The x position of the click on the dragged object
22853      * @param {Number} y The y position of the click on the dragged object
22854      * @return {Boolean} true to continue the drag, false to cancel
22855      */
22856     onInitDrag : function(x, y){
22857         this.proxy.update(this.dragData.ddel.cloneNode(true));
22858         this.onStartDrag(x, y);
22859         return true;
22860     },
22861     
22862     /**
22863      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22864      */
22865     afterRepair : function(){
22866         if(Roo.enableFx){
22867             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22868         }
22869         this.dragging = false;
22870     },
22871
22872     /**
22873      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22874      * the XY of this.dragData.ddel
22875      * @param {EventObject} e The mouse up event
22876      * @return {Array} The xy location (e.g. [100, 200])
22877      */
22878     getRepairXY : function(e){
22879         return Roo.Element.fly(this.dragData.ddel).getXY();  
22880     }
22881 });/*
22882  * Based on:
22883  * Ext JS Library 1.1.1
22884  * Copyright(c) 2006-2007, Ext JS, LLC.
22885  *
22886  * Originally Released Under LGPL - original licence link has changed is not relivant.
22887  *
22888  * Fork - LGPL
22889  * <script type="text/javascript">
22890  */
22891 /**
22892  * @class Roo.dd.DropZone
22893  * @extends Roo.dd.DropTarget
22894  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22895  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22896  * @constructor
22897  * @param {String/HTMLElement/Element} el The container element
22898  * @param {Object} config
22899  */
22900 Roo.dd.DropZone = function(el, config){
22901     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22902 };
22903
22904 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22905     /**
22906      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22907      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22908      * provide your own custom lookup.
22909      * @param {Event} e The event
22910      * @return {Object} data The custom data
22911      */
22912     getTargetFromEvent : function(e){
22913         return Roo.dd.Registry.getTargetFromEvent(e);
22914     },
22915
22916     /**
22917      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22918      * that it has registered.  This method has no default implementation and should be overridden to provide
22919      * node-specific processing if necessary.
22920      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22921      * {@link #getTargetFromEvent} for this node)
22922      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22923      * @param {Event} e The event
22924      * @param {Object} data An object containing arbitrary data supplied by the drag source
22925      */
22926     onNodeEnter : function(n, dd, e, data){
22927         
22928     },
22929
22930     /**
22931      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22932      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22933      * overridden to provide the proper feedback.
22934      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22935      * {@link #getTargetFromEvent} for this node)
22936      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22937      * @param {Event} e The event
22938      * @param {Object} data An object containing arbitrary data supplied by the drag source
22939      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22940      * underlying {@link Roo.dd.StatusProxy} can be updated
22941      */
22942     onNodeOver : function(n, dd, e, data){
22943         return this.dropAllowed;
22944     },
22945
22946     /**
22947      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22948      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22949      * node-specific processing if necessary.
22950      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22951      * {@link #getTargetFromEvent} for this node)
22952      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22953      * @param {Event} e The event
22954      * @param {Object} data An object containing arbitrary data supplied by the drag source
22955      */
22956     onNodeOut : function(n, dd, e, data){
22957         
22958     },
22959
22960     /**
22961      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22962      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22963      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22964      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22965      * {@link #getTargetFromEvent} for this node)
22966      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22967      * @param {Event} e The event
22968      * @param {Object} data An object containing arbitrary data supplied by the drag source
22969      * @return {Boolean} True if the drop was valid, else false
22970      */
22971     onNodeDrop : function(n, dd, e, data){
22972         return false;
22973     },
22974
22975     /**
22976      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22977      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22978      * it should be overridden to provide the proper feedback if necessary.
22979      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22980      * @param {Event} e The event
22981      * @param {Object} data An object containing arbitrary data supplied by the drag source
22982      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22983      * underlying {@link Roo.dd.StatusProxy} can be updated
22984      */
22985     onContainerOver : function(dd, e, data){
22986         return this.dropNotAllowed;
22987     },
22988
22989     /**
22990      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22991      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22992      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22993      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22994      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22995      * @param {Event} e The event
22996      * @param {Object} data An object containing arbitrary data supplied by the drag source
22997      * @return {Boolean} True if the drop was valid, else false
22998      */
22999     onContainerDrop : function(dd, e, data){
23000         return false;
23001     },
23002
23003     /**
23004      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23005      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23006      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23007      * you should override this method and provide a custom implementation.
23008      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23009      * @param {Event} e The event
23010      * @param {Object} data An object containing arbitrary data supplied by the drag source
23011      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23012      * underlying {@link Roo.dd.StatusProxy} can be updated
23013      */
23014     notifyEnter : function(dd, e, data){
23015         return this.dropNotAllowed;
23016     },
23017
23018     /**
23019      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23020      * This method will be called on every mouse movement while the drag source is over the drop zone.
23021      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23022      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23023      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23024      * registered node, it will call {@link #onContainerOver}.
23025      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23026      * @param {Event} e The event
23027      * @param {Object} data An object containing arbitrary data supplied by the drag source
23028      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23029      * underlying {@link Roo.dd.StatusProxy} can be updated
23030      */
23031     notifyOver : function(dd, e, data){
23032         var n = this.getTargetFromEvent(e);
23033         if(!n){ // not over valid drop target
23034             if(this.lastOverNode){
23035                 this.onNodeOut(this.lastOverNode, dd, e, data);
23036                 this.lastOverNode = null;
23037             }
23038             return this.onContainerOver(dd, e, data);
23039         }
23040         if(this.lastOverNode != n){
23041             if(this.lastOverNode){
23042                 this.onNodeOut(this.lastOverNode, dd, e, data);
23043             }
23044             this.onNodeEnter(n, dd, e, data);
23045             this.lastOverNode = n;
23046         }
23047         return this.onNodeOver(n, dd, e, data);
23048     },
23049
23050     /**
23051      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23052      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23053      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23054      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23055      * @param {Event} e The event
23056      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23057      */
23058     notifyOut : function(dd, e, data){
23059         if(this.lastOverNode){
23060             this.onNodeOut(this.lastOverNode, dd, e, data);
23061             this.lastOverNode = null;
23062         }
23063     },
23064
23065     /**
23066      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23067      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23068      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23069      * otherwise it will call {@link #onContainerDrop}.
23070      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23071      * @param {Event} e The event
23072      * @param {Object} data An object containing arbitrary data supplied by the drag source
23073      * @return {Boolean} True if the drop was valid, else false
23074      */
23075     notifyDrop : function(dd, e, data){
23076         if(this.lastOverNode){
23077             this.onNodeOut(this.lastOverNode, dd, e, data);
23078             this.lastOverNode = null;
23079         }
23080         var n = this.getTargetFromEvent(e);
23081         return n ?
23082             this.onNodeDrop(n, dd, e, data) :
23083             this.onContainerDrop(dd, e, data);
23084     },
23085
23086     // private
23087     triggerCacheRefresh : function(){
23088         Roo.dd.DDM.refreshCache(this.groups);
23089     }  
23090 });/*
23091  * Based on:
23092  * Ext JS Library 1.1.1
23093  * Copyright(c) 2006-2007, Ext JS, LLC.
23094  *
23095  * Originally Released Under LGPL - original licence link has changed is not relivant.
23096  *
23097  * Fork - LGPL
23098  * <script type="text/javascript">
23099  */
23100
23101
23102 /**
23103  * @class Roo.data.SortTypes
23104  * @singleton
23105  * Defines the default sorting (casting?) comparison functions used when sorting data.
23106  */
23107 Roo.data.SortTypes = {
23108     /**
23109      * Default sort that does nothing
23110      * @param {Mixed} s The value being converted
23111      * @return {Mixed} The comparison value
23112      */
23113     none : function(s){
23114         return s;
23115     },
23116     
23117     /**
23118      * The regular expression used to strip tags
23119      * @type {RegExp}
23120      * @property
23121      */
23122     stripTagsRE : /<\/?[^>]+>/gi,
23123     
23124     /**
23125      * Strips all HTML tags to sort on text only
23126      * @param {Mixed} s The value being converted
23127      * @return {String} The comparison value
23128      */
23129     asText : function(s){
23130         return String(s).replace(this.stripTagsRE, "");
23131     },
23132     
23133     /**
23134      * Strips all HTML tags to sort on text only - Case insensitive
23135      * @param {Mixed} s The value being converted
23136      * @return {String} The comparison value
23137      */
23138     asUCText : function(s){
23139         return String(s).toUpperCase().replace(this.stripTagsRE, "");
23140     },
23141     
23142     /**
23143      * Case insensitive string
23144      * @param {Mixed} s The value being converted
23145      * @return {String} The comparison value
23146      */
23147     asUCString : function(s) {
23148         return String(s).toUpperCase();
23149     },
23150     
23151     /**
23152      * Date sorting
23153      * @param {Mixed} s The value being converted
23154      * @return {Number} The comparison value
23155      */
23156     asDate : function(s) {
23157         if(!s){
23158             return 0;
23159         }
23160         if(s instanceof Date){
23161             return s.getTime();
23162         }
23163         return Date.parse(String(s));
23164     },
23165     
23166     /**
23167      * Float sorting
23168      * @param {Mixed} s The value being converted
23169      * @return {Float} The comparison value
23170      */
23171     asFloat : function(s) {
23172         var val = parseFloat(String(s).replace(/,/g, ""));
23173         if(isNaN(val)) {
23174             val = 0;
23175         }
23176         return val;
23177     },
23178     
23179     /**
23180      * Integer sorting
23181      * @param {Mixed} s The value being converted
23182      * @return {Number} The comparison value
23183      */
23184     asInt : function(s) {
23185         var val = parseInt(String(s).replace(/,/g, ""));
23186         if(isNaN(val)) {
23187             val = 0;
23188         }
23189         return val;
23190     }
23191 };/*
23192  * Based on:
23193  * Ext JS Library 1.1.1
23194  * Copyright(c) 2006-2007, Ext JS, LLC.
23195  *
23196  * Originally Released Under LGPL - original licence link has changed is not relivant.
23197  *
23198  * Fork - LGPL
23199  * <script type="text/javascript">
23200  */
23201
23202 /**
23203 * @class Roo.data.Record
23204  * Instances of this class encapsulate both record <em>definition</em> information, and record
23205  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
23206  * to access Records cached in an {@link Roo.data.Store} object.<br>
23207  * <p>
23208  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
23209  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
23210  * objects.<br>
23211  * <p>
23212  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
23213  * @constructor
23214  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
23215  * {@link #create}. The parameters are the same.
23216  * @param {Array} data An associative Array of data values keyed by the field name.
23217  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
23218  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
23219  * not specified an integer id is generated.
23220  */
23221 Roo.data.Record = function(data, id){
23222     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
23223     this.data = data;
23224 };
23225
23226 /**
23227  * Generate a constructor for a specific record layout.
23228  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
23229  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
23230  * Each field definition object may contain the following properties: <ul>
23231  * <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,
23232  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
23233  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
23234  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
23235  * is being used, then this is a string containing the javascript expression to reference the data relative to 
23236  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
23237  * to the data item relative to the record element. If the mapping expression is the same as the field name,
23238  * this may be omitted.</p></li>
23239  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
23240  * <ul><li>auto (Default, implies no conversion)</li>
23241  * <li>string</li>
23242  * <li>int</li>
23243  * <li>float</li>
23244  * <li>boolean</li>
23245  * <li>date</li></ul></p></li>
23246  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
23247  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
23248  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
23249  * by the Reader into an object that will be stored in the Record. It is passed the
23250  * following parameters:<ul>
23251  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
23252  * </ul></p></li>
23253  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
23254  * </ul>
23255  * <br>usage:<br><pre><code>
23256 var TopicRecord = Roo.data.Record.create(
23257     {name: 'title', mapping: 'topic_title'},
23258     {name: 'author', mapping: 'username'},
23259     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
23260     {name: 'lastPost', mapping: 'post_time', type: 'date'},
23261     {name: 'lastPoster', mapping: 'user2'},
23262     {name: 'excerpt', mapping: 'post_text'}
23263 );
23264
23265 var myNewRecord = new TopicRecord({
23266     title: 'Do my job please',
23267     author: 'noobie',
23268     totalPosts: 1,
23269     lastPost: new Date(),
23270     lastPoster: 'Animal',
23271     excerpt: 'No way dude!'
23272 });
23273 myStore.add(myNewRecord);
23274 </code></pre>
23275  * @method create
23276  * @static
23277  */
23278 Roo.data.Record.create = function(o){
23279     var f = function(){
23280         f.superclass.constructor.apply(this, arguments);
23281     };
23282     Roo.extend(f, Roo.data.Record);
23283     var p = f.prototype;
23284     p.fields = new Roo.util.MixedCollection(false, function(field){
23285         return field.name;
23286     });
23287     for(var i = 0, len = o.length; i < len; i++){
23288         p.fields.add(new Roo.data.Field(o[i]));
23289     }
23290     f.getField = function(name){
23291         return p.fields.get(name);  
23292     };
23293     return f;
23294 };
23295
23296 Roo.data.Record.AUTO_ID = 1000;
23297 Roo.data.Record.EDIT = 'edit';
23298 Roo.data.Record.REJECT = 'reject';
23299 Roo.data.Record.COMMIT = 'commit';
23300
23301 Roo.data.Record.prototype = {
23302     /**
23303      * Readonly flag - true if this record has been modified.
23304      * @type Boolean
23305      */
23306     dirty : false,
23307     editing : false,
23308     error: null,
23309     modified: null,
23310
23311     // private
23312     join : function(store){
23313         this.store = store;
23314     },
23315
23316     /**
23317      * Set the named field to the specified value.
23318      * @param {String} name The name of the field to set.
23319      * @param {Object} value The value to set the field to.
23320      */
23321     set : function(name, value){
23322         if(this.data[name] == value){
23323             return;
23324         }
23325         this.dirty = true;
23326         if(!this.modified){
23327             this.modified = {};
23328         }
23329         if(typeof this.modified[name] == 'undefined'){
23330             this.modified[name] = this.data[name];
23331         }
23332         this.data[name] = value;
23333         if(!this.editing && this.store){
23334             this.store.afterEdit(this);
23335         }       
23336     },
23337
23338     /**
23339      * Get the value of the named field.
23340      * @param {String} name The name of the field to get the value of.
23341      * @return {Object} The value of the field.
23342      */
23343     get : function(name){
23344         return this.data[name]; 
23345     },
23346
23347     // private
23348     beginEdit : function(){
23349         this.editing = true;
23350         this.modified = {}; 
23351     },
23352
23353     // private
23354     cancelEdit : function(){
23355         this.editing = false;
23356         delete this.modified;
23357     },
23358
23359     // private
23360     endEdit : function(){
23361         this.editing = false;
23362         if(this.dirty && this.store){
23363             this.store.afterEdit(this);
23364         }
23365     },
23366
23367     /**
23368      * Usually called by the {@link Roo.data.Store} which owns the Record.
23369      * Rejects all changes made to the Record since either creation, or the last commit operation.
23370      * Modified fields are reverted to their original values.
23371      * <p>
23372      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23373      * of reject operations.
23374      */
23375     reject : function(){
23376         var m = this.modified;
23377         for(var n in m){
23378             if(typeof m[n] != "function"){
23379                 this.data[n] = m[n];
23380             }
23381         }
23382         this.dirty = false;
23383         delete this.modified;
23384         this.editing = false;
23385         if(this.store){
23386             this.store.afterReject(this);
23387         }
23388     },
23389
23390     /**
23391      * Usually called by the {@link Roo.data.Store} which owns the Record.
23392      * Commits all changes made to the Record since either creation, or the last commit operation.
23393      * <p>
23394      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23395      * of commit operations.
23396      */
23397     commit : function(){
23398         this.dirty = false;
23399         delete this.modified;
23400         this.editing = false;
23401         if(this.store){
23402             this.store.afterCommit(this);
23403         }
23404     },
23405
23406     // private
23407     hasError : function(){
23408         return this.error != null;
23409     },
23410
23411     // private
23412     clearError : function(){
23413         this.error = null;
23414     },
23415
23416     /**
23417      * Creates a copy of this record.
23418      * @param {String} id (optional) A new record id if you don't want to use this record's id
23419      * @return {Record}
23420      */
23421     copy : function(newId) {
23422         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
23423     }
23424 };/*
23425  * Based on:
23426  * Ext JS Library 1.1.1
23427  * Copyright(c) 2006-2007, Ext JS, LLC.
23428  *
23429  * Originally Released Under LGPL - original licence link has changed is not relivant.
23430  *
23431  * Fork - LGPL
23432  * <script type="text/javascript">
23433  */
23434
23435
23436
23437 /**
23438  * @class Roo.data.Store
23439  * @extends Roo.util.Observable
23440  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
23441  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
23442  * <p>
23443  * 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
23444  * has no knowledge of the format of the data returned by the Proxy.<br>
23445  * <p>
23446  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
23447  * instances from the data object. These records are cached and made available through accessor functions.
23448  * @constructor
23449  * Creates a new Store.
23450  * @param {Object} config A config object containing the objects needed for the Store to access data,
23451  * and read the data into Records.
23452  */
23453 Roo.data.Store = function(config){
23454     this.data = new Roo.util.MixedCollection(false);
23455     this.data.getKey = function(o){
23456         return o.id;
23457     };
23458     this.baseParams = {};
23459     // private
23460     this.paramNames = {
23461         "start" : "start",
23462         "limit" : "limit",
23463         "sort" : "sort",
23464         "dir" : "dir",
23465         "multisort" : "_multisort"
23466     };
23467
23468     if(config && config.data){
23469         this.inlineData = config.data;
23470         delete config.data;
23471     }
23472
23473     Roo.apply(this, config);
23474     
23475     if(this.reader){ // reader passed
23476         this.reader = Roo.factory(this.reader, Roo.data);
23477         this.reader.xmodule = this.xmodule || false;
23478         if(!this.recordType){
23479             this.recordType = this.reader.recordType;
23480         }
23481         if(this.reader.onMetaChange){
23482             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
23483         }
23484     }
23485
23486     if(this.recordType){
23487         this.fields = this.recordType.prototype.fields;
23488     }
23489     this.modified = [];
23490
23491     this.addEvents({
23492         /**
23493          * @event datachanged
23494          * Fires when the data cache has changed, and a widget which is using this Store
23495          * as a Record cache should refresh its view.
23496          * @param {Store} this
23497          */
23498         datachanged : true,
23499         /**
23500          * @event metachange
23501          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
23502          * @param {Store} this
23503          * @param {Object} meta The JSON metadata
23504          */
23505         metachange : true,
23506         /**
23507          * @event add
23508          * Fires when Records have been added to the Store
23509          * @param {Store} this
23510          * @param {Roo.data.Record[]} records The array of Records added
23511          * @param {Number} index The index at which the record(s) were added
23512          */
23513         add : true,
23514         /**
23515          * @event remove
23516          * Fires when a Record has been removed from the Store
23517          * @param {Store} this
23518          * @param {Roo.data.Record} record The Record that was removed
23519          * @param {Number} index The index at which the record was removed
23520          */
23521         remove : true,
23522         /**
23523          * @event update
23524          * Fires when a Record has been updated
23525          * @param {Store} this
23526          * @param {Roo.data.Record} record The Record that was updated
23527          * @param {String} operation The update operation being performed.  Value may be one of:
23528          * <pre><code>
23529  Roo.data.Record.EDIT
23530  Roo.data.Record.REJECT
23531  Roo.data.Record.COMMIT
23532          * </code></pre>
23533          */
23534         update : true,
23535         /**
23536          * @event clear
23537          * Fires when the data cache has been cleared.
23538          * @param {Store} this
23539          */
23540         clear : true,
23541         /**
23542          * @event beforeload
23543          * Fires before a request is made for a new data object.  If the beforeload handler returns false
23544          * the load action will be canceled.
23545          * @param {Store} this
23546          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23547          */
23548         beforeload : true,
23549         /**
23550          * @event beforeloadadd
23551          * Fires after a new set of Records has been loaded.
23552          * @param {Store} this
23553          * @param {Roo.data.Record[]} records The Records that were loaded
23554          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23555          */
23556         beforeloadadd : true,
23557         /**
23558          * @event load
23559          * Fires after a new set of Records has been loaded, before they are added to the store.
23560          * @param {Store} this
23561          * @param {Roo.data.Record[]} records The Records that were loaded
23562          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23563          * @params {Object} return from reader
23564          */
23565         load : true,
23566         /**
23567          * @event loadexception
23568          * Fires if an exception occurs in the Proxy during loading.
23569          * Called with the signature of the Proxy's "loadexception" event.
23570          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
23571          * 
23572          * @param {Proxy} 
23573          * @param {Object} return from JsonData.reader() - success, totalRecords, records
23574          * @param {Object} load options 
23575          * @param {Object} jsonData from your request (normally this contains the Exception)
23576          */
23577         loadexception : true
23578     });
23579     
23580     if(this.proxy){
23581         this.proxy = Roo.factory(this.proxy, Roo.data);
23582         this.proxy.xmodule = this.xmodule || false;
23583         this.relayEvents(this.proxy,  ["loadexception"]);
23584     }
23585     this.sortToggle = {};
23586     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
23587
23588     Roo.data.Store.superclass.constructor.call(this);
23589
23590     if(this.inlineData){
23591         this.loadData(this.inlineData);
23592         delete this.inlineData;
23593     }
23594 };
23595
23596 Roo.extend(Roo.data.Store, Roo.util.Observable, {
23597      /**
23598     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
23599     * without a remote query - used by combo/forms at present.
23600     */
23601     
23602     /**
23603     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
23604     */
23605     /**
23606     * @cfg {Array} data Inline data to be loaded when the store is initialized.
23607     */
23608     /**
23609     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
23610     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
23611     */
23612     /**
23613     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
23614     * on any HTTP request
23615     */
23616     /**
23617     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
23618     */
23619     /**
23620     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
23621     */
23622     multiSort: false,
23623     /**
23624     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
23625     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
23626     */
23627     remoteSort : false,
23628
23629     /**
23630     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23631      * loaded or when a record is removed. (defaults to false).
23632     */
23633     pruneModifiedRecords : false,
23634
23635     // private
23636     lastOptions : null,
23637
23638     /**
23639      * Add Records to the Store and fires the add event.
23640      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23641      */
23642     add : function(records){
23643         records = [].concat(records);
23644         for(var i = 0, len = records.length; i < len; i++){
23645             records[i].join(this);
23646         }
23647         var index = this.data.length;
23648         this.data.addAll(records);
23649         this.fireEvent("add", this, records, index);
23650     },
23651
23652     /**
23653      * Remove a Record from the Store and fires the remove event.
23654      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23655      */
23656     remove : function(record){
23657         var index = this.data.indexOf(record);
23658         this.data.removeAt(index);
23659  
23660         if(this.pruneModifiedRecords){
23661             this.modified.remove(record);
23662         }
23663         this.fireEvent("remove", this, record, index);
23664     },
23665
23666     /**
23667      * Remove all Records from the Store and fires the clear event.
23668      */
23669     removeAll : function(){
23670         this.data.clear();
23671         if(this.pruneModifiedRecords){
23672             this.modified = [];
23673         }
23674         this.fireEvent("clear", this);
23675     },
23676
23677     /**
23678      * Inserts Records to the Store at the given index and fires the add event.
23679      * @param {Number} index The start index at which to insert the passed Records.
23680      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23681      */
23682     insert : function(index, records){
23683         records = [].concat(records);
23684         for(var i = 0, len = records.length; i < len; i++){
23685             this.data.insert(index, records[i]);
23686             records[i].join(this);
23687         }
23688         this.fireEvent("add", this, records, index);
23689     },
23690
23691     /**
23692      * Get the index within the cache of the passed Record.
23693      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23694      * @return {Number} The index of the passed Record. Returns -1 if not found.
23695      */
23696     indexOf : function(record){
23697         return this.data.indexOf(record);
23698     },
23699
23700     /**
23701      * Get the index within the cache of the Record with the passed id.
23702      * @param {String} id The id of the Record to find.
23703      * @return {Number} The index of the Record. Returns -1 if not found.
23704      */
23705     indexOfId : function(id){
23706         return this.data.indexOfKey(id);
23707     },
23708
23709     /**
23710      * Get the Record with the specified id.
23711      * @param {String} id The id of the Record to find.
23712      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23713      */
23714     getById : function(id){
23715         return this.data.key(id);
23716     },
23717
23718     /**
23719      * Get the Record at the specified index.
23720      * @param {Number} index The index of the Record to find.
23721      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23722      */
23723     getAt : function(index){
23724         return this.data.itemAt(index);
23725     },
23726
23727     /**
23728      * Returns a range of Records between specified indices.
23729      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23730      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23731      * @return {Roo.data.Record[]} An array of Records
23732      */
23733     getRange : function(start, end){
23734         return this.data.getRange(start, end);
23735     },
23736
23737     // private
23738     storeOptions : function(o){
23739         o = Roo.apply({}, o);
23740         delete o.callback;
23741         delete o.scope;
23742         this.lastOptions = o;
23743     },
23744
23745     /**
23746      * Loads the Record cache from the configured Proxy using the configured Reader.
23747      * <p>
23748      * If using remote paging, then the first load call must specify the <em>start</em>
23749      * and <em>limit</em> properties in the options.params property to establish the initial
23750      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23751      * <p>
23752      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23753      * and this call will return before the new data has been loaded. Perform any post-processing
23754      * in a callback function, or in a "load" event handler.</strong>
23755      * <p>
23756      * @param {Object} options An object containing properties which control loading options:<ul>
23757      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23758      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23759      * passed the following arguments:<ul>
23760      * <li>r : Roo.data.Record[]</li>
23761      * <li>options: Options object from the load call</li>
23762      * <li>success: Boolean success indicator</li></ul></li>
23763      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23764      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23765      * </ul>
23766      */
23767     load : function(options){
23768         options = options || {};
23769         if(this.fireEvent("beforeload", this, options) !== false){
23770             this.storeOptions(options);
23771             var p = Roo.apply(options.params || {}, this.baseParams);
23772             // if meta was not loaded from remote source.. try requesting it.
23773             if (!this.reader.metaFromRemote) {
23774                 p._requestMeta = 1;
23775             }
23776             if(this.sortInfo && this.remoteSort){
23777                 var pn = this.paramNames;
23778                 p[pn["sort"]] = this.sortInfo.field;
23779                 p[pn["dir"]] = this.sortInfo.direction;
23780             }
23781             if (this.multiSort) {
23782                 var pn = this.paramNames;
23783                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23784             }
23785             
23786             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23787         }
23788     },
23789
23790     /**
23791      * Reloads the Record cache from the configured Proxy using the configured Reader and
23792      * the options from the last load operation performed.
23793      * @param {Object} options (optional) An object containing properties which may override the options
23794      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23795      * the most recently used options are reused).
23796      */
23797     reload : function(options){
23798         this.load(Roo.applyIf(options||{}, this.lastOptions));
23799     },
23800
23801     // private
23802     // Called as a callback by the Reader during a load operation.
23803     loadRecords : function(o, options, success){
23804         if(!o || success === false){
23805             if(success !== false){
23806                 this.fireEvent("load", this, [], options, o);
23807             }
23808             if(options.callback){
23809                 options.callback.call(options.scope || this, [], options, false);
23810             }
23811             return;
23812         }
23813         // if data returned failure - throw an exception.
23814         if (o.success === false) {
23815             // show a message if no listener is registered.
23816             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23817                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23818             }
23819             // loadmask wil be hooked into this..
23820             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23821             return;
23822         }
23823         var r = o.records, t = o.totalRecords || r.length;
23824         
23825         this.fireEvent("beforeloadadd", this, r, options, o);
23826         
23827         if(!options || options.add !== true){
23828             if(this.pruneModifiedRecords){
23829                 this.modified = [];
23830             }
23831             for(var i = 0, len = r.length; i < len; i++){
23832                 r[i].join(this);
23833             }
23834             if(this.snapshot){
23835                 this.data = this.snapshot;
23836                 delete this.snapshot;
23837             }
23838             this.data.clear();
23839             this.data.addAll(r);
23840             this.totalLength = t;
23841             this.applySort();
23842             this.fireEvent("datachanged", this);
23843         }else{
23844             this.totalLength = Math.max(t, this.data.length+r.length);
23845             this.add(r);
23846         }
23847         
23848         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23849                 
23850             var e = new Roo.data.Record({});
23851
23852             e.set(this.parent.displayField, this.parent.emptyTitle);
23853             e.set(this.parent.valueField, '');
23854
23855             this.insert(0, e);
23856         }
23857             
23858         this.fireEvent("load", this, r, options, o);
23859         if(options.callback){
23860             options.callback.call(options.scope || this, r, options, true);
23861         }
23862     },
23863
23864
23865     /**
23866      * Loads data from a passed data block. A Reader which understands the format of the data
23867      * must have been configured in the constructor.
23868      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23869      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23870      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23871      */
23872     loadData : function(o, append){
23873         var r = this.reader.readRecords(o);
23874         this.loadRecords(r, {add: append}, true);
23875     },
23876     
23877      /**
23878      * using 'cn' the nested child reader read the child array into it's child stores.
23879      * @param {Object} rec The record with a 'children array
23880      */
23881     loadDataFromChildren : function(rec)
23882     {
23883         this.loadData(this.reader.toLoadData(rec));
23884     },
23885     
23886
23887     /**
23888      * Gets the number of cached records.
23889      * <p>
23890      * <em>If using paging, this may not be the total size of the dataset. If the data object
23891      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23892      * the data set size</em>
23893      */
23894     getCount : function(){
23895         return this.data.length || 0;
23896     },
23897
23898     /**
23899      * Gets the total number of records in the dataset as returned by the server.
23900      * <p>
23901      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23902      * the dataset size</em>
23903      */
23904     getTotalCount : function(){
23905         return this.totalLength || 0;
23906     },
23907
23908     /**
23909      * Returns the sort state of the Store as an object with two properties:
23910      * <pre><code>
23911  field {String} The name of the field by which the Records are sorted
23912  direction {String} The sort order, "ASC" or "DESC"
23913      * </code></pre>
23914      */
23915     getSortState : function(){
23916         return this.sortInfo;
23917     },
23918
23919     // private
23920     applySort : function(){
23921         if(this.sortInfo && !this.remoteSort){
23922             var s = this.sortInfo, f = s.field;
23923             var st = this.fields.get(f).sortType;
23924             var fn = function(r1, r2){
23925                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23926                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23927             };
23928             this.data.sort(s.direction, fn);
23929             if(this.snapshot && this.snapshot != this.data){
23930                 this.snapshot.sort(s.direction, fn);
23931             }
23932         }
23933     },
23934
23935     /**
23936      * Sets the default sort column and order to be used by the next load operation.
23937      * @param {String} fieldName The name of the field to sort by.
23938      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23939      */
23940     setDefaultSort : function(field, dir){
23941         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23942     },
23943
23944     /**
23945      * Sort the Records.
23946      * If remote sorting is used, the sort is performed on the server, and the cache is
23947      * reloaded. If local sorting is used, the cache is sorted internally.
23948      * @param {String} fieldName The name of the field to sort by.
23949      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23950      */
23951     sort : function(fieldName, dir){
23952         var f = this.fields.get(fieldName);
23953         if(!dir){
23954             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23955             
23956             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23957                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23958             }else{
23959                 dir = f.sortDir;
23960             }
23961         }
23962         this.sortToggle[f.name] = dir;
23963         this.sortInfo = {field: f.name, direction: dir};
23964         if(!this.remoteSort){
23965             this.applySort();
23966             this.fireEvent("datachanged", this);
23967         }else{
23968             this.load(this.lastOptions);
23969         }
23970     },
23971
23972     /**
23973      * Calls the specified function for each of the Records in the cache.
23974      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23975      * Returning <em>false</em> aborts and exits the iteration.
23976      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23977      */
23978     each : function(fn, scope){
23979         this.data.each(fn, scope);
23980     },
23981
23982     /**
23983      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23984      * (e.g., during paging).
23985      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23986      */
23987     getModifiedRecords : function(){
23988         return this.modified;
23989     },
23990
23991     // private
23992     createFilterFn : function(property, value, anyMatch){
23993         if(!value.exec){ // not a regex
23994             value = String(value);
23995             if(value.length == 0){
23996                 return false;
23997             }
23998             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23999         }
24000         return function(r){
24001             return value.test(r.data[property]);
24002         };
24003     },
24004
24005     /**
24006      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24007      * @param {String} property A field on your records
24008      * @param {Number} start The record index to start at (defaults to 0)
24009      * @param {Number} end The last record index to include (defaults to length - 1)
24010      * @return {Number} The sum
24011      */
24012     sum : function(property, start, end){
24013         var rs = this.data.items, v = 0;
24014         start = start || 0;
24015         end = (end || end === 0) ? end : rs.length-1;
24016
24017         for(var i = start; i <= end; i++){
24018             v += (rs[i].data[property] || 0);
24019         }
24020         return v;
24021     },
24022
24023     /**
24024      * Filter the records by a specified property.
24025      * @param {String} field A field on your records
24026      * @param {String/RegExp} value Either a string that the field
24027      * should start with or a RegExp to test against the field
24028      * @param {Boolean} anyMatch True to match any part not just the beginning
24029      */
24030     filter : function(property, value, anyMatch){
24031         var fn = this.createFilterFn(property, value, anyMatch);
24032         return fn ? this.filterBy(fn) : this.clearFilter();
24033     },
24034
24035     /**
24036      * Filter by a function. The specified function will be called with each
24037      * record in this data source. If the function returns true the record is included,
24038      * otherwise it is filtered.
24039      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24040      * @param {Object} scope (optional) The scope of the function (defaults to this)
24041      */
24042     filterBy : function(fn, scope){
24043         this.snapshot = this.snapshot || this.data;
24044         this.data = this.queryBy(fn, scope||this);
24045         this.fireEvent("datachanged", this);
24046     },
24047
24048     /**
24049      * Query the records by a specified property.
24050      * @param {String} field A field on your records
24051      * @param {String/RegExp} value Either a string that the field
24052      * should start with or a RegExp to test against the field
24053      * @param {Boolean} anyMatch True to match any part not just the beginning
24054      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24055      */
24056     query : function(property, value, anyMatch){
24057         var fn = this.createFilterFn(property, value, anyMatch);
24058         return fn ? this.queryBy(fn) : this.data.clone();
24059     },
24060
24061     /**
24062      * Query by a function. The specified function will be called with each
24063      * record in this data source. If the function returns true the record is included
24064      * in the results.
24065      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24066      * @param {Object} scope (optional) The scope of the function (defaults to this)
24067       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24068      **/
24069     queryBy : function(fn, scope){
24070         var data = this.snapshot || this.data;
24071         return data.filterBy(fn, scope||this);
24072     },
24073
24074     /**
24075      * Collects unique values for a particular dataIndex from this store.
24076      * @param {String} dataIndex The property to collect
24077      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24078      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24079      * @return {Array} An array of the unique values
24080      **/
24081     collect : function(dataIndex, allowNull, bypassFilter){
24082         var d = (bypassFilter === true && this.snapshot) ?
24083                 this.snapshot.items : this.data.items;
24084         var v, sv, r = [], l = {};
24085         for(var i = 0, len = d.length; i < len; i++){
24086             v = d[i].data[dataIndex];
24087             sv = String(v);
24088             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24089                 l[sv] = true;
24090                 r[r.length] = v;
24091             }
24092         }
24093         return r;
24094     },
24095
24096     /**
24097      * Revert to a view of the Record cache with no filtering applied.
24098      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24099      */
24100     clearFilter : function(suppressEvent){
24101         if(this.snapshot && this.snapshot != this.data){
24102             this.data = this.snapshot;
24103             delete this.snapshot;
24104             if(suppressEvent !== true){
24105                 this.fireEvent("datachanged", this);
24106             }
24107         }
24108     },
24109
24110     // private
24111     afterEdit : function(record){
24112         if(this.modified.indexOf(record) == -1){
24113             this.modified.push(record);
24114         }
24115         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24116     },
24117     
24118     // private
24119     afterReject : function(record){
24120         this.modified.remove(record);
24121         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
24122     },
24123
24124     // private
24125     afterCommit : function(record){
24126         this.modified.remove(record);
24127         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
24128     },
24129
24130     /**
24131      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
24132      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
24133      */
24134     commitChanges : function(){
24135         var m = this.modified.slice(0);
24136         this.modified = [];
24137         for(var i = 0, len = m.length; i < len; i++){
24138             m[i].commit();
24139         }
24140     },
24141
24142     /**
24143      * Cancel outstanding changes on all changed records.
24144      */
24145     rejectChanges : function(){
24146         var m = this.modified.slice(0);
24147         this.modified = [];
24148         for(var i = 0, len = m.length; i < len; i++){
24149             m[i].reject();
24150         }
24151     },
24152
24153     onMetaChange : function(meta, rtype, o){
24154         this.recordType = rtype;
24155         this.fields = rtype.prototype.fields;
24156         delete this.snapshot;
24157         this.sortInfo = meta.sortInfo || this.sortInfo;
24158         this.modified = [];
24159         this.fireEvent('metachange', this, this.reader.meta);
24160     },
24161     
24162     moveIndex : function(data, type)
24163     {
24164         var index = this.indexOf(data);
24165         
24166         var newIndex = index + type;
24167         
24168         this.remove(data);
24169         
24170         this.insert(newIndex, data);
24171         
24172     }
24173 });/*
24174  * Based on:
24175  * Ext JS Library 1.1.1
24176  * Copyright(c) 2006-2007, Ext JS, LLC.
24177  *
24178  * Originally Released Under LGPL - original licence link has changed is not relivant.
24179  *
24180  * Fork - LGPL
24181  * <script type="text/javascript">
24182  */
24183
24184 /**
24185  * @class Roo.data.SimpleStore
24186  * @extends Roo.data.Store
24187  * Small helper class to make creating Stores from Array data easier.
24188  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
24189  * @cfg {Array} fields An array of field definition objects, or field name strings.
24190  * @cfg {Object} an existing reader (eg. copied from another store)
24191  * @cfg {Array} data The multi-dimensional array of data
24192  * @constructor
24193  * @param {Object} config
24194  */
24195 Roo.data.SimpleStore = function(config)
24196 {
24197     Roo.data.SimpleStore.superclass.constructor.call(this, {
24198         isLocal : true,
24199         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
24200                 id: config.id
24201             },
24202             Roo.data.Record.create(config.fields)
24203         ),
24204         proxy : new Roo.data.MemoryProxy(config.data)
24205     });
24206     this.load();
24207 };
24208 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
24209  * Based on:
24210  * Ext JS Library 1.1.1
24211  * Copyright(c) 2006-2007, Ext JS, LLC.
24212  *
24213  * Originally Released Under LGPL - original licence link has changed is not relivant.
24214  *
24215  * Fork - LGPL
24216  * <script type="text/javascript">
24217  */
24218
24219 /**
24220 /**
24221  * @extends Roo.data.Store
24222  * @class Roo.data.JsonStore
24223  * Small helper class to make creating Stores for JSON data easier. <br/>
24224 <pre><code>
24225 var store = new Roo.data.JsonStore({
24226     url: 'get-images.php',
24227     root: 'images',
24228     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
24229 });
24230 </code></pre>
24231  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
24232  * JsonReader and HttpProxy (unless inline data is provided).</b>
24233  * @cfg {Array} fields An array of field definition objects, or field name strings.
24234  * @constructor
24235  * @param {Object} config
24236  */
24237 Roo.data.JsonStore = function(c){
24238     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
24239         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
24240         reader: new Roo.data.JsonReader(c, c.fields)
24241     }));
24242 };
24243 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
24244  * Based on:
24245  * Ext JS Library 1.1.1
24246  * Copyright(c) 2006-2007, Ext JS, LLC.
24247  *
24248  * Originally Released Under LGPL - original licence link has changed is not relivant.
24249  *
24250  * Fork - LGPL
24251  * <script type="text/javascript">
24252  */
24253
24254  
24255 Roo.data.Field = function(config){
24256     if(typeof config == "string"){
24257         config = {name: config};
24258     }
24259     Roo.apply(this, config);
24260     
24261     if(!this.type){
24262         this.type = "auto";
24263     }
24264     
24265     var st = Roo.data.SortTypes;
24266     // named sortTypes are supported, here we look them up
24267     if(typeof this.sortType == "string"){
24268         this.sortType = st[this.sortType];
24269     }
24270     
24271     // set default sortType for strings and dates
24272     if(!this.sortType){
24273         switch(this.type){
24274             case "string":
24275                 this.sortType = st.asUCString;
24276                 break;
24277             case "date":
24278                 this.sortType = st.asDate;
24279                 break;
24280             default:
24281                 this.sortType = st.none;
24282         }
24283     }
24284
24285     // define once
24286     var stripRe = /[\$,%]/g;
24287
24288     // prebuilt conversion function for this field, instead of
24289     // switching every time we're reading a value
24290     if(!this.convert){
24291         var cv, dateFormat = this.dateFormat;
24292         switch(this.type){
24293             case "":
24294             case "auto":
24295             case undefined:
24296                 cv = function(v){ return v; };
24297                 break;
24298             case "string":
24299                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
24300                 break;
24301             case "int":
24302                 cv = function(v){
24303                     return v !== undefined && v !== null && v !== '' ?
24304                            parseInt(String(v).replace(stripRe, ""), 10) : '';
24305                     };
24306                 break;
24307             case "float":
24308                 cv = function(v){
24309                     return v !== undefined && v !== null && v !== '' ?
24310                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
24311                     };
24312                 break;
24313             case "bool":
24314             case "boolean":
24315                 cv = function(v){ return v === true || v === "true" || v == 1; };
24316                 break;
24317             case "date":
24318                 cv = function(v){
24319                     if(!v){
24320                         return '';
24321                     }
24322                     if(v instanceof Date){
24323                         return v;
24324                     }
24325                     if(dateFormat){
24326                         if(dateFormat == "timestamp"){
24327                             return new Date(v*1000);
24328                         }
24329                         return Date.parseDate(v, dateFormat);
24330                     }
24331                     var parsed = Date.parse(v);
24332                     return parsed ? new Date(parsed) : null;
24333                 };
24334              break;
24335             
24336         }
24337         this.convert = cv;
24338     }
24339 };
24340
24341 Roo.data.Field.prototype = {
24342     dateFormat: null,
24343     defaultValue: "",
24344     mapping: null,
24345     sortType : null,
24346     sortDir : "ASC"
24347 };/*
24348  * Based on:
24349  * Ext JS Library 1.1.1
24350  * Copyright(c) 2006-2007, Ext JS, LLC.
24351  *
24352  * Originally Released Under LGPL - original licence link has changed is not relivant.
24353  *
24354  * Fork - LGPL
24355  * <script type="text/javascript">
24356  */
24357  
24358 // Base class for reading structured data from a data source.  This class is intended to be
24359 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
24360
24361 /**
24362  * @class Roo.data.DataReader
24363  * Base class for reading structured data from a data source.  This class is intended to be
24364  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
24365  */
24366
24367 Roo.data.DataReader = function(meta, recordType){
24368     
24369     this.meta = meta;
24370     
24371     this.recordType = recordType instanceof Array ? 
24372         Roo.data.Record.create(recordType) : recordType;
24373 };
24374
24375 Roo.data.DataReader.prototype = {
24376     
24377     
24378     readerType : 'Data',
24379      /**
24380      * Create an empty record
24381      * @param {Object} data (optional) - overlay some values
24382      * @return {Roo.data.Record} record created.
24383      */
24384     newRow :  function(d) {
24385         var da =  {};
24386         this.recordType.prototype.fields.each(function(c) {
24387             switch( c.type) {
24388                 case 'int' : da[c.name] = 0; break;
24389                 case 'date' : da[c.name] = new Date(); break;
24390                 case 'float' : da[c.name] = 0.0; break;
24391                 case 'boolean' : da[c.name] = false; break;
24392                 default : da[c.name] = ""; break;
24393             }
24394             
24395         });
24396         return new this.recordType(Roo.apply(da, d));
24397     }
24398     
24399     
24400 };/*
24401  * Based on:
24402  * Ext JS Library 1.1.1
24403  * Copyright(c) 2006-2007, Ext JS, LLC.
24404  *
24405  * Originally Released Under LGPL - original licence link has changed is not relivant.
24406  *
24407  * Fork - LGPL
24408  * <script type="text/javascript">
24409  */
24410
24411 /**
24412  * @class Roo.data.DataProxy
24413  * @extends Roo.data.Observable
24414  * This class is an abstract base class for implementations which provide retrieval of
24415  * unformatted data objects.<br>
24416  * <p>
24417  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
24418  * (of the appropriate type which knows how to parse the data object) to provide a block of
24419  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
24420  * <p>
24421  * Custom implementations must implement the load method as described in
24422  * {@link Roo.data.HttpProxy#load}.
24423  */
24424 Roo.data.DataProxy = function(){
24425     this.addEvents({
24426         /**
24427          * @event beforeload
24428          * Fires before a network request is made to retrieve a data object.
24429          * @param {Object} This DataProxy object.
24430          * @param {Object} params The params parameter to the load function.
24431          */
24432         beforeload : true,
24433         /**
24434          * @event load
24435          * Fires before the load method's callback is called.
24436          * @param {Object} This DataProxy object.
24437          * @param {Object} o The data object.
24438          * @param {Object} arg The callback argument object passed to the load function.
24439          */
24440         load : true,
24441         /**
24442          * @event loadexception
24443          * Fires if an Exception occurs during data retrieval.
24444          * @param {Object} This DataProxy object.
24445          * @param {Object} o The data object.
24446          * @param {Object} arg The callback argument object passed to the load function.
24447          * @param {Object} e The Exception.
24448          */
24449         loadexception : true
24450     });
24451     Roo.data.DataProxy.superclass.constructor.call(this);
24452 };
24453
24454 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
24455
24456     /**
24457      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
24458      */
24459 /*
24460  * Based on:
24461  * Ext JS Library 1.1.1
24462  * Copyright(c) 2006-2007, Ext JS, LLC.
24463  *
24464  * Originally Released Under LGPL - original licence link has changed is not relivant.
24465  *
24466  * Fork - LGPL
24467  * <script type="text/javascript">
24468  */
24469 /**
24470  * @class Roo.data.MemoryProxy
24471  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
24472  * to the Reader when its load method is called.
24473  * @constructor
24474  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
24475  */
24476 Roo.data.MemoryProxy = function(data){
24477     if (data.data) {
24478         data = data.data;
24479     }
24480     Roo.data.MemoryProxy.superclass.constructor.call(this);
24481     this.data = data;
24482 };
24483
24484 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
24485     
24486     /**
24487      * Load data from the requested source (in this case an in-memory
24488      * data object passed to the constructor), read the data object into
24489      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24490      * process that block using the passed callback.
24491      * @param {Object} params This parameter is not used by the MemoryProxy class.
24492      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24493      * object into a block of Roo.data.Records.
24494      * @param {Function} callback The function into which to pass the block of Roo.data.records.
24495      * The function must be passed <ul>
24496      * <li>The Record block object</li>
24497      * <li>The "arg" argument from the load function</li>
24498      * <li>A boolean success indicator</li>
24499      * </ul>
24500      * @param {Object} scope The scope in which to call the callback
24501      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24502      */
24503     load : function(params, reader, callback, scope, arg){
24504         params = params || {};
24505         var result;
24506         try {
24507             result = reader.readRecords(params.data ? params.data :this.data);
24508         }catch(e){
24509             this.fireEvent("loadexception", this, arg, null, e);
24510             callback.call(scope, null, arg, false);
24511             return;
24512         }
24513         callback.call(scope, result, arg, true);
24514     },
24515     
24516     // private
24517     update : function(params, records){
24518         
24519     }
24520 });/*
24521  * Based on:
24522  * Ext JS Library 1.1.1
24523  * Copyright(c) 2006-2007, Ext JS, LLC.
24524  *
24525  * Originally Released Under LGPL - original licence link has changed is not relivant.
24526  *
24527  * Fork - LGPL
24528  * <script type="text/javascript">
24529  */
24530 /**
24531  * @class Roo.data.HttpProxy
24532  * @extends Roo.data.DataProxy
24533  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
24534  * configured to reference a certain URL.<br><br>
24535  * <p>
24536  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
24537  * from which the running page was served.<br><br>
24538  * <p>
24539  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
24540  * <p>
24541  * Be aware that to enable the browser to parse an XML document, the server must set
24542  * the Content-Type header in the HTTP response to "text/xml".
24543  * @constructor
24544  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
24545  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
24546  * will be used to make the request.
24547  */
24548 Roo.data.HttpProxy = function(conn){
24549     Roo.data.HttpProxy.superclass.constructor.call(this);
24550     // is conn a conn config or a real conn?
24551     this.conn = conn;
24552     this.useAjax = !conn || !conn.events;
24553   
24554 };
24555
24556 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
24557     // thse are take from connection...
24558     
24559     /**
24560      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
24561      */
24562     /**
24563      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
24564      * extra parameters to each request made by this object. (defaults to undefined)
24565      */
24566     /**
24567      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
24568      *  to each request made by this object. (defaults to undefined)
24569      */
24570     /**
24571      * @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)
24572      */
24573     /**
24574      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
24575      */
24576      /**
24577      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
24578      * @type Boolean
24579      */
24580   
24581
24582     /**
24583      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
24584      * @type Boolean
24585      */
24586     /**
24587      * Return the {@link Roo.data.Connection} object being used by this Proxy.
24588      * @return {Connection} The Connection object. This object may be used to subscribe to events on
24589      * a finer-grained basis than the DataProxy events.
24590      */
24591     getConnection : function(){
24592         return this.useAjax ? Roo.Ajax : this.conn;
24593     },
24594
24595     /**
24596      * Load data from the configured {@link Roo.data.Connection}, read the data object into
24597      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
24598      * process that block using the passed callback.
24599      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24600      * for the request to the remote server.
24601      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24602      * object into a block of Roo.data.Records.
24603      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24604      * The function must be passed <ul>
24605      * <li>The Record block object</li>
24606      * <li>The "arg" argument from the load function</li>
24607      * <li>A boolean success indicator</li>
24608      * </ul>
24609      * @param {Object} scope The scope in which to call the callback
24610      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24611      */
24612     load : function(params, reader, callback, scope, arg){
24613         if(this.fireEvent("beforeload", this, params) !== false){
24614             var  o = {
24615                 params : params || {},
24616                 request: {
24617                     callback : callback,
24618                     scope : scope,
24619                     arg : arg
24620                 },
24621                 reader: reader,
24622                 callback : this.loadResponse,
24623                 scope: this
24624             };
24625             if(this.useAjax){
24626                 Roo.applyIf(o, this.conn);
24627                 if(this.activeRequest){
24628                     Roo.Ajax.abort(this.activeRequest);
24629                 }
24630                 this.activeRequest = Roo.Ajax.request(o);
24631             }else{
24632                 this.conn.request(o);
24633             }
24634         }else{
24635             callback.call(scope||this, null, arg, false);
24636         }
24637     },
24638
24639     // private
24640     loadResponse : function(o, success, response){
24641         delete this.activeRequest;
24642         if(!success){
24643             this.fireEvent("loadexception", this, o, response);
24644             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24645             return;
24646         }
24647         var result;
24648         try {
24649             result = o.reader.read(response);
24650         }catch(e){
24651             this.fireEvent("loadexception", this, o, response, e);
24652             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24653             return;
24654         }
24655         
24656         this.fireEvent("load", this, o, o.request.arg);
24657         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24658     },
24659
24660     // private
24661     update : function(dataSet){
24662
24663     },
24664
24665     // private
24666     updateResponse : function(dataSet){
24667
24668     }
24669 });/*
24670  * Based on:
24671  * Ext JS Library 1.1.1
24672  * Copyright(c) 2006-2007, Ext JS, LLC.
24673  *
24674  * Originally Released Under LGPL - original licence link has changed is not relivant.
24675  *
24676  * Fork - LGPL
24677  * <script type="text/javascript">
24678  */
24679
24680 /**
24681  * @class Roo.data.ScriptTagProxy
24682  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24683  * other than the originating domain of the running page.<br><br>
24684  * <p>
24685  * <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
24686  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24687  * <p>
24688  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24689  * source code that is used as the source inside a &lt;script> tag.<br><br>
24690  * <p>
24691  * In order for the browser to process the returned data, the server must wrap the data object
24692  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24693  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24694  * depending on whether the callback name was passed:
24695  * <p>
24696  * <pre><code>
24697 boolean scriptTag = false;
24698 String cb = request.getParameter("callback");
24699 if (cb != null) {
24700     scriptTag = true;
24701     response.setContentType("text/javascript");
24702 } else {
24703     response.setContentType("application/x-json");
24704 }
24705 Writer out = response.getWriter();
24706 if (scriptTag) {
24707     out.write(cb + "(");
24708 }
24709 out.print(dataBlock.toJsonString());
24710 if (scriptTag) {
24711     out.write(");");
24712 }
24713 </pre></code>
24714  *
24715  * @constructor
24716  * @param {Object} config A configuration object.
24717  */
24718 Roo.data.ScriptTagProxy = function(config){
24719     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24720     Roo.apply(this, config);
24721     this.head = document.getElementsByTagName("head")[0];
24722 };
24723
24724 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24725
24726 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24727     /**
24728      * @cfg {String} url The URL from which to request the data object.
24729      */
24730     /**
24731      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24732      */
24733     timeout : 30000,
24734     /**
24735      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24736      * the server the name of the callback function set up by the load call to process the returned data object.
24737      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24738      * javascript output which calls this named function passing the data object as its only parameter.
24739      */
24740     callbackParam : "callback",
24741     /**
24742      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24743      * name to the request.
24744      */
24745     nocache : true,
24746
24747     /**
24748      * Load data from the configured URL, read the data object into
24749      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24750      * process that block using the passed callback.
24751      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24752      * for the request to the remote server.
24753      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24754      * object into a block of Roo.data.Records.
24755      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24756      * The function must be passed <ul>
24757      * <li>The Record block object</li>
24758      * <li>The "arg" argument from the load function</li>
24759      * <li>A boolean success indicator</li>
24760      * </ul>
24761      * @param {Object} scope The scope in which to call the callback
24762      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24763      */
24764     load : function(params, reader, callback, scope, arg){
24765         if(this.fireEvent("beforeload", this, params) !== false){
24766
24767             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24768
24769             var url = this.url;
24770             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24771             if(this.nocache){
24772                 url += "&_dc=" + (new Date().getTime());
24773             }
24774             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24775             var trans = {
24776                 id : transId,
24777                 cb : "stcCallback"+transId,
24778                 scriptId : "stcScript"+transId,
24779                 params : params,
24780                 arg : arg,
24781                 url : url,
24782                 callback : callback,
24783                 scope : scope,
24784                 reader : reader
24785             };
24786             var conn = this;
24787
24788             window[trans.cb] = function(o){
24789                 conn.handleResponse(o, trans);
24790             };
24791
24792             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24793
24794             if(this.autoAbort !== false){
24795                 this.abort();
24796             }
24797
24798             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24799
24800             var script = document.createElement("script");
24801             script.setAttribute("src", url);
24802             script.setAttribute("type", "text/javascript");
24803             script.setAttribute("id", trans.scriptId);
24804             this.head.appendChild(script);
24805
24806             this.trans = trans;
24807         }else{
24808             callback.call(scope||this, null, arg, false);
24809         }
24810     },
24811
24812     // private
24813     isLoading : function(){
24814         return this.trans ? true : false;
24815     },
24816
24817     /**
24818      * Abort the current server request.
24819      */
24820     abort : function(){
24821         if(this.isLoading()){
24822             this.destroyTrans(this.trans);
24823         }
24824     },
24825
24826     // private
24827     destroyTrans : function(trans, isLoaded){
24828         this.head.removeChild(document.getElementById(trans.scriptId));
24829         clearTimeout(trans.timeoutId);
24830         if(isLoaded){
24831             window[trans.cb] = undefined;
24832             try{
24833                 delete window[trans.cb];
24834             }catch(e){}
24835         }else{
24836             // if hasn't been loaded, wait for load to remove it to prevent script error
24837             window[trans.cb] = function(){
24838                 window[trans.cb] = undefined;
24839                 try{
24840                     delete window[trans.cb];
24841                 }catch(e){}
24842             };
24843         }
24844     },
24845
24846     // private
24847     handleResponse : function(o, trans){
24848         this.trans = false;
24849         this.destroyTrans(trans, true);
24850         var result;
24851         try {
24852             result = trans.reader.readRecords(o);
24853         }catch(e){
24854             this.fireEvent("loadexception", this, o, trans.arg, e);
24855             trans.callback.call(trans.scope||window, null, trans.arg, false);
24856             return;
24857         }
24858         this.fireEvent("load", this, o, trans.arg);
24859         trans.callback.call(trans.scope||window, result, trans.arg, true);
24860     },
24861
24862     // private
24863     handleFailure : function(trans){
24864         this.trans = false;
24865         this.destroyTrans(trans, false);
24866         this.fireEvent("loadexception", this, null, trans.arg);
24867         trans.callback.call(trans.scope||window, null, trans.arg, false);
24868     }
24869 });/*
24870  * Based on:
24871  * Ext JS Library 1.1.1
24872  * Copyright(c) 2006-2007, Ext JS, LLC.
24873  *
24874  * Originally Released Under LGPL - original licence link has changed is not relivant.
24875  *
24876  * Fork - LGPL
24877  * <script type="text/javascript">
24878  */
24879
24880 /**
24881  * @class Roo.data.JsonReader
24882  * @extends Roo.data.DataReader
24883  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24884  * based on mappings in a provided Roo.data.Record constructor.
24885  * 
24886  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24887  * in the reply previously. 
24888  * 
24889  * <p>
24890  * Example code:
24891  * <pre><code>
24892 var RecordDef = Roo.data.Record.create([
24893     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24894     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24895 ]);
24896 var myReader = new Roo.data.JsonReader({
24897     totalProperty: "results",    // The property which contains the total dataset size (optional)
24898     root: "rows",                // The property which contains an Array of row objects
24899     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24900 }, RecordDef);
24901 </code></pre>
24902  * <p>
24903  * This would consume a JSON file like this:
24904  * <pre><code>
24905 { 'results': 2, 'rows': [
24906     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24907     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24908 }
24909 </code></pre>
24910  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24911  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24912  * paged from the remote server.
24913  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24914  * @cfg {String} root name of the property which contains the Array of row objects.
24915  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24916  * @cfg {Array} fields Array of field definition objects
24917  * @constructor
24918  * Create a new JsonReader
24919  * @param {Object} meta Metadata configuration options
24920  * @param {Object} recordType Either an Array of field definition objects,
24921  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24922  */
24923 Roo.data.JsonReader = function(meta, recordType){
24924     
24925     meta = meta || {};
24926     // set some defaults:
24927     Roo.applyIf(meta, {
24928         totalProperty: 'total',
24929         successProperty : 'success',
24930         root : 'data',
24931         id : 'id'
24932     });
24933     
24934     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24935 };
24936 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24937     
24938     readerType : 'Json',
24939     
24940     /**
24941      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24942      * Used by Store query builder to append _requestMeta to params.
24943      * 
24944      */
24945     metaFromRemote : false,
24946     /**
24947      * This method is only used by a DataProxy which has retrieved data from a remote server.
24948      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24949      * @return {Object} data A data block which is used by an Roo.data.Store object as
24950      * a cache of Roo.data.Records.
24951      */
24952     read : function(response){
24953         var json = response.responseText;
24954        
24955         var o = /* eval:var:o */ eval("("+json+")");
24956         if(!o) {
24957             throw {message: "JsonReader.read: Json object not found"};
24958         }
24959         
24960         if(o.metaData){
24961             
24962             delete this.ef;
24963             this.metaFromRemote = true;
24964             this.meta = o.metaData;
24965             this.recordType = Roo.data.Record.create(o.metaData.fields);
24966             this.onMetaChange(this.meta, this.recordType, o);
24967         }
24968         return this.readRecords(o);
24969     },
24970
24971     // private function a store will implement
24972     onMetaChange : function(meta, recordType, o){
24973
24974     },
24975
24976     /**
24977          * @ignore
24978          */
24979     simpleAccess: function(obj, subsc) {
24980         return obj[subsc];
24981     },
24982
24983         /**
24984          * @ignore
24985          */
24986     getJsonAccessor: function(){
24987         var re = /[\[\.]/;
24988         return function(expr) {
24989             try {
24990                 return(re.test(expr))
24991                     ? new Function("obj", "return obj." + expr)
24992                     : function(obj){
24993                         return obj[expr];
24994                     };
24995             } catch(e){}
24996             return Roo.emptyFn;
24997         };
24998     }(),
24999
25000     /**
25001      * Create a data block containing Roo.data.Records from an XML document.
25002      * @param {Object} o An object which contains an Array of row objects in the property specified
25003      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25004      * which contains the total size of the dataset.
25005      * @return {Object} data A data block which is used by an Roo.data.Store object as
25006      * a cache of Roo.data.Records.
25007      */
25008     readRecords : function(o){
25009         /**
25010          * After any data loads, the raw JSON data is available for further custom processing.
25011          * @type Object
25012          */
25013         this.o = o;
25014         var s = this.meta, Record = this.recordType,
25015             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25016
25017 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25018         if (!this.ef) {
25019             if(s.totalProperty) {
25020                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25021                 }
25022                 if(s.successProperty) {
25023                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25024                 }
25025                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25026                 if (s.id) {
25027                         var g = this.getJsonAccessor(s.id);
25028                         this.getId = function(rec) {
25029                                 var r = g(rec);  
25030                                 return (r === undefined || r === "") ? null : r;
25031                         };
25032                 } else {
25033                         this.getId = function(){return null;};
25034                 }
25035             this.ef = [];
25036             for(var jj = 0; jj < fl; jj++){
25037                 f = fi[jj];
25038                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25039                 this.ef[jj] = this.getJsonAccessor(map);
25040             }
25041         }
25042
25043         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25044         if(s.totalProperty){
25045             var vt = parseInt(this.getTotal(o), 10);
25046             if(!isNaN(vt)){
25047                 totalRecords = vt;
25048             }
25049         }
25050         if(s.successProperty){
25051             var vs = this.getSuccess(o);
25052             if(vs === false || vs === 'false'){
25053                 success = false;
25054             }
25055         }
25056         var records = [];
25057         for(var i = 0; i < c; i++){
25058                 var n = root[i];
25059             var values = {};
25060             var id = this.getId(n);
25061             for(var j = 0; j < fl; j++){
25062                 f = fi[j];
25063             var v = this.ef[j](n);
25064             if (!f.convert) {
25065                 Roo.log('missing convert for ' + f.name);
25066                 Roo.log(f);
25067                 continue;
25068             }
25069             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25070             }
25071             var record = new Record(values, id);
25072             record.json = n;
25073             records[i] = record;
25074         }
25075         return {
25076             raw : o,
25077             success : success,
25078             records : records,
25079             totalRecords : totalRecords
25080         };
25081     },
25082     // used when loading children.. @see loadDataFromChildren
25083     toLoadData: function(rec)
25084     {
25085         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25086         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25087         return { data : data, total : data.length };
25088         
25089     }
25090 });/*
25091  * Based on:
25092  * Ext JS Library 1.1.1
25093  * Copyright(c) 2006-2007, Ext JS, LLC.
25094  *
25095  * Originally Released Under LGPL - original licence link has changed is not relivant.
25096  *
25097  * Fork - LGPL
25098  * <script type="text/javascript">
25099  */
25100
25101 /**
25102  * @class Roo.data.XmlReader
25103  * @extends Roo.data.DataReader
25104  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25105  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25106  * <p>
25107  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
25108  * header in the HTTP response must be set to "text/xml".</em>
25109  * <p>
25110  * Example code:
25111  * <pre><code>
25112 var RecordDef = Roo.data.Record.create([
25113    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25114    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25115 ]);
25116 var myReader = new Roo.data.XmlReader({
25117    totalRecords: "results", // The element which contains the total dataset size (optional)
25118    record: "row",           // The repeated element which contains row information
25119    id: "id"                 // The element within the row that provides an ID for the record (optional)
25120 }, RecordDef);
25121 </code></pre>
25122  * <p>
25123  * This would consume an XML file like this:
25124  * <pre><code>
25125 &lt;?xml?>
25126 &lt;dataset>
25127  &lt;results>2&lt;/results>
25128  &lt;row>
25129    &lt;id>1&lt;/id>
25130    &lt;name>Bill&lt;/name>
25131    &lt;occupation>Gardener&lt;/occupation>
25132  &lt;/row>
25133  &lt;row>
25134    &lt;id>2&lt;/id>
25135    &lt;name>Ben&lt;/name>
25136    &lt;occupation>Horticulturalist&lt;/occupation>
25137  &lt;/row>
25138 &lt;/dataset>
25139 </code></pre>
25140  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25141  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25142  * paged from the remote server.
25143  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25144  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25145  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25146  * a record identifier value.
25147  * @constructor
25148  * Create a new XmlReader
25149  * @param {Object} meta Metadata configuration options
25150  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25151  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25152  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25153  */
25154 Roo.data.XmlReader = function(meta, recordType){
25155     meta = meta || {};
25156     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25157 };
25158 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25159     
25160     readerType : 'Xml',
25161     
25162     /**
25163      * This method is only used by a DataProxy which has retrieved data from a remote server.
25164          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25165          * to contain a method called 'responseXML' that returns an XML document object.
25166      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25167      * a cache of Roo.data.Records.
25168      */
25169     read : function(response){
25170         var doc = response.responseXML;
25171         if(!doc) {
25172             throw {message: "XmlReader.read: XML Document not available"};
25173         }
25174         return this.readRecords(doc);
25175     },
25176
25177     /**
25178      * Create a data block containing Roo.data.Records from an XML document.
25179          * @param {Object} doc A parsed XML document.
25180      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25181      * a cache of Roo.data.Records.
25182      */
25183     readRecords : function(doc){
25184         /**
25185          * After any data loads/reads, the raw XML Document is available for further custom processing.
25186          * @type XMLDocument
25187          */
25188         this.xmlData = doc;
25189         var root = doc.documentElement || doc;
25190         var q = Roo.DomQuery;
25191         var recordType = this.recordType, fields = recordType.prototype.fields;
25192         var sid = this.meta.id;
25193         var totalRecords = 0, success = true;
25194         if(this.meta.totalRecords){
25195             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
25196         }
25197         
25198         if(this.meta.success){
25199             var sv = q.selectValue(this.meta.success, root, true);
25200             success = sv !== false && sv !== 'false';
25201         }
25202         var records = [];
25203         var ns = q.select(this.meta.record, root);
25204         for(var i = 0, len = ns.length; i < len; i++) {
25205                 var n = ns[i];
25206                 var values = {};
25207                 var id = sid ? q.selectValue(sid, n) : undefined;
25208                 for(var j = 0, jlen = fields.length; j < jlen; j++){
25209                     var f = fields.items[j];
25210                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
25211                     v = f.convert(v);
25212                     values[f.name] = v;
25213                 }
25214                 var record = new recordType(values, id);
25215                 record.node = n;
25216                 records[records.length] = record;
25217             }
25218
25219             return {
25220                 success : success,
25221                 records : records,
25222                 totalRecords : totalRecords || records.length
25223             };
25224     }
25225 });/*
25226  * Based on:
25227  * Ext JS Library 1.1.1
25228  * Copyright(c) 2006-2007, Ext JS, LLC.
25229  *
25230  * Originally Released Under LGPL - original licence link has changed is not relivant.
25231  *
25232  * Fork - LGPL
25233  * <script type="text/javascript">
25234  */
25235
25236 /**
25237  * @class Roo.data.ArrayReader
25238  * @extends Roo.data.DataReader
25239  * Data reader class to create an Array of Roo.data.Record objects from an Array.
25240  * Each element of that Array represents a row of data fields. The
25241  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
25242  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
25243  * <p>
25244  * Example code:.
25245  * <pre><code>
25246 var RecordDef = Roo.data.Record.create([
25247     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
25248     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
25249 ]);
25250 var myReader = new Roo.data.ArrayReader({
25251     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
25252 }, RecordDef);
25253 </code></pre>
25254  * <p>
25255  * This would consume an Array like this:
25256  * <pre><code>
25257 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
25258   </code></pre>
25259  
25260  * @constructor
25261  * Create a new JsonReader
25262  * @param {Object} meta Metadata configuration options.
25263  * @param {Object|Array} recordType Either an Array of field definition objects
25264  * 
25265  * @cfg {Array} fields Array of field definition objects
25266  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25267  * as specified to {@link Roo.data.Record#create},
25268  * or an {@link Roo.data.Record} object
25269  *
25270  * 
25271  * created using {@link Roo.data.Record#create}.
25272  */
25273 Roo.data.ArrayReader = function(meta, recordType)
25274 {    
25275     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25276 };
25277
25278 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
25279     
25280       /**
25281      * Create a data block containing Roo.data.Records from an XML document.
25282      * @param {Object} o An Array of row objects which represents the dataset.
25283      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
25284      * a cache of Roo.data.Records.
25285      */
25286     readRecords : function(o)
25287     {
25288         var sid = this.meta ? this.meta.id : null;
25289         var recordType = this.recordType, fields = recordType.prototype.fields;
25290         var records = [];
25291         var root = o;
25292         for(var i = 0; i < root.length; i++){
25293             var n = root[i];
25294             var values = {};
25295             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
25296             for(var j = 0, jlen = fields.length; j < jlen; j++){
25297                 var f = fields.items[j];
25298                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
25299                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
25300                 v = f.convert(v);
25301                 values[f.name] = v;
25302             }
25303             var record = new recordType(values, id);
25304             record.json = n;
25305             records[records.length] = record;
25306         }
25307         return {
25308             records : records,
25309             totalRecords : records.length
25310         };
25311     },
25312     // used when loading children.. @see loadDataFromChildren
25313     toLoadData: function(rec)
25314     {
25315         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25316         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25317         
25318     }
25319     
25320     
25321 });/*
25322  * Based on:
25323  * Ext JS Library 1.1.1
25324  * Copyright(c) 2006-2007, Ext JS, LLC.
25325  *
25326  * Originally Released Under LGPL - original licence link has changed is not relivant.
25327  *
25328  * Fork - LGPL
25329  * <script type="text/javascript">
25330  */
25331
25332
25333 /**
25334  * @class Roo.data.Tree
25335  * @extends Roo.util.Observable
25336  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
25337  * in the tree have most standard DOM functionality.
25338  * @constructor
25339  * @param {Node} root (optional) The root node
25340  */
25341 Roo.data.Tree = function(root){
25342    this.nodeHash = {};
25343    /**
25344     * The root node for this tree
25345     * @type Node
25346     */
25347    this.root = null;
25348    if(root){
25349        this.setRootNode(root);
25350    }
25351    this.addEvents({
25352        /**
25353         * @event append
25354         * Fires when a new child node is appended to a node in this tree.
25355         * @param {Tree} tree The owner tree
25356         * @param {Node} parent The parent node
25357         * @param {Node} node The newly appended node
25358         * @param {Number} index The index of the newly appended node
25359         */
25360        "append" : true,
25361        /**
25362         * @event remove
25363         * Fires when a child node is removed from a node in this tree.
25364         * @param {Tree} tree The owner tree
25365         * @param {Node} parent The parent node
25366         * @param {Node} node The child node removed
25367         */
25368        "remove" : true,
25369        /**
25370         * @event move
25371         * Fires when a node is moved to a new location in the tree
25372         * @param {Tree} tree The owner tree
25373         * @param {Node} node The node moved
25374         * @param {Node} oldParent The old parent of this node
25375         * @param {Node} newParent The new parent of this node
25376         * @param {Number} index The index it was moved to
25377         */
25378        "move" : true,
25379        /**
25380         * @event insert
25381         * Fires when a new child node is inserted in a node in this tree.
25382         * @param {Tree} tree The owner tree
25383         * @param {Node} parent The parent node
25384         * @param {Node} node The child node inserted
25385         * @param {Node} refNode The child node the node was inserted before
25386         */
25387        "insert" : true,
25388        /**
25389         * @event beforeappend
25390         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
25391         * @param {Tree} tree The owner tree
25392         * @param {Node} parent The parent node
25393         * @param {Node} node The child node to be appended
25394         */
25395        "beforeappend" : true,
25396        /**
25397         * @event beforeremove
25398         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
25399         * @param {Tree} tree The owner tree
25400         * @param {Node} parent The parent node
25401         * @param {Node} node The child node to be removed
25402         */
25403        "beforeremove" : true,
25404        /**
25405         * @event beforemove
25406         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
25407         * @param {Tree} tree The owner tree
25408         * @param {Node} node The node being moved
25409         * @param {Node} oldParent The parent of the node
25410         * @param {Node} newParent The new parent the node is moving to
25411         * @param {Number} index The index it is being moved to
25412         */
25413        "beforemove" : true,
25414        /**
25415         * @event beforeinsert
25416         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
25417         * @param {Tree} tree The owner tree
25418         * @param {Node} parent The parent node
25419         * @param {Node} node The child node to be inserted
25420         * @param {Node} refNode The child node the node is being inserted before
25421         */
25422        "beforeinsert" : true
25423    });
25424
25425     Roo.data.Tree.superclass.constructor.call(this);
25426 };
25427
25428 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
25429     pathSeparator: "/",
25430
25431     proxyNodeEvent : function(){
25432         return this.fireEvent.apply(this, arguments);
25433     },
25434
25435     /**
25436      * Returns the root node for this tree.
25437      * @return {Node}
25438      */
25439     getRootNode : function(){
25440         return this.root;
25441     },
25442
25443     /**
25444      * Sets the root node for this tree.
25445      * @param {Node} node
25446      * @return {Node}
25447      */
25448     setRootNode : function(node){
25449         this.root = node;
25450         node.ownerTree = this;
25451         node.isRoot = true;
25452         this.registerNode(node);
25453         return node;
25454     },
25455
25456     /**
25457      * Gets a node in this tree by its id.
25458      * @param {String} id
25459      * @return {Node}
25460      */
25461     getNodeById : function(id){
25462         return this.nodeHash[id];
25463     },
25464
25465     registerNode : function(node){
25466         this.nodeHash[node.id] = node;
25467     },
25468
25469     unregisterNode : function(node){
25470         delete this.nodeHash[node.id];
25471     },
25472
25473     toString : function(){
25474         return "[Tree"+(this.id?" "+this.id:"")+"]";
25475     }
25476 });
25477
25478 /**
25479  * @class Roo.data.Node
25480  * @extends Roo.util.Observable
25481  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
25482  * @cfg {String} id The id for this node. If one is not specified, one is generated.
25483  * @constructor
25484  * @param {Object} attributes The attributes/config for the node
25485  */
25486 Roo.data.Node = function(attributes){
25487     /**
25488      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
25489      * @type {Object}
25490      */
25491     this.attributes = attributes || {};
25492     this.leaf = this.attributes.leaf;
25493     /**
25494      * The node id. @type String
25495      */
25496     this.id = this.attributes.id;
25497     if(!this.id){
25498         this.id = Roo.id(null, "ynode-");
25499         this.attributes.id = this.id;
25500     }
25501      
25502     
25503     /**
25504      * All child nodes of this node. @type Array
25505      */
25506     this.childNodes = [];
25507     if(!this.childNodes.indexOf){ // indexOf is a must
25508         this.childNodes.indexOf = function(o){
25509             for(var i = 0, len = this.length; i < len; i++){
25510                 if(this[i] == o) {
25511                     return i;
25512                 }
25513             }
25514             return -1;
25515         };
25516     }
25517     /**
25518      * The parent node for this node. @type Node
25519      */
25520     this.parentNode = null;
25521     /**
25522      * The first direct child node of this node, or null if this node has no child nodes. @type Node
25523      */
25524     this.firstChild = null;
25525     /**
25526      * The last direct child node of this node, or null if this node has no child nodes. @type Node
25527      */
25528     this.lastChild = null;
25529     /**
25530      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
25531      */
25532     this.previousSibling = null;
25533     /**
25534      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
25535      */
25536     this.nextSibling = null;
25537
25538     this.addEvents({
25539        /**
25540         * @event append
25541         * Fires when a new child node is appended
25542         * @param {Tree} tree The owner tree
25543         * @param {Node} this This node
25544         * @param {Node} node The newly appended node
25545         * @param {Number} index The index of the newly appended node
25546         */
25547        "append" : true,
25548        /**
25549         * @event remove
25550         * Fires when a child node is removed
25551         * @param {Tree} tree The owner tree
25552         * @param {Node} this This node
25553         * @param {Node} node The removed node
25554         */
25555        "remove" : true,
25556        /**
25557         * @event move
25558         * Fires when this node is moved to a new location in the tree
25559         * @param {Tree} tree The owner tree
25560         * @param {Node} this This node
25561         * @param {Node} oldParent The old parent of this node
25562         * @param {Node} newParent The new parent of this node
25563         * @param {Number} index The index it was moved to
25564         */
25565        "move" : true,
25566        /**
25567         * @event insert
25568         * Fires when a new child node is inserted.
25569         * @param {Tree} tree The owner tree
25570         * @param {Node} this This node
25571         * @param {Node} node The child node inserted
25572         * @param {Node} refNode The child node the node was inserted before
25573         */
25574        "insert" : true,
25575        /**
25576         * @event beforeappend
25577         * Fires before a new child is appended, return false to cancel the append.
25578         * @param {Tree} tree The owner tree
25579         * @param {Node} this This node
25580         * @param {Node} node The child node to be appended
25581         */
25582        "beforeappend" : true,
25583        /**
25584         * @event beforeremove
25585         * Fires before a child is removed, return false to cancel the remove.
25586         * @param {Tree} tree The owner tree
25587         * @param {Node} this This node
25588         * @param {Node} node The child node to be removed
25589         */
25590        "beforeremove" : true,
25591        /**
25592         * @event beforemove
25593         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
25594         * @param {Tree} tree The owner tree
25595         * @param {Node} this This node
25596         * @param {Node} oldParent The parent of this node
25597         * @param {Node} newParent The new parent this node is moving to
25598         * @param {Number} index The index it is being moved to
25599         */
25600        "beforemove" : true,
25601        /**
25602         * @event beforeinsert
25603         * Fires before a new child is inserted, return false to cancel the insert.
25604         * @param {Tree} tree The owner tree
25605         * @param {Node} this This node
25606         * @param {Node} node The child node to be inserted
25607         * @param {Node} refNode The child node the node is being inserted before
25608         */
25609        "beforeinsert" : true
25610    });
25611     this.listeners = this.attributes.listeners;
25612     Roo.data.Node.superclass.constructor.call(this);
25613 };
25614
25615 Roo.extend(Roo.data.Node, Roo.util.Observable, {
25616     fireEvent : function(evtName){
25617         // first do standard event for this node
25618         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
25619             return false;
25620         }
25621         // then bubble it up to the tree if the event wasn't cancelled
25622         var ot = this.getOwnerTree();
25623         if(ot){
25624             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
25625                 return false;
25626             }
25627         }
25628         return true;
25629     },
25630
25631     /**
25632      * Returns true if this node is a leaf
25633      * @return {Boolean}
25634      */
25635     isLeaf : function(){
25636         return this.leaf === true;
25637     },
25638
25639     // private
25640     setFirstChild : function(node){
25641         this.firstChild = node;
25642     },
25643
25644     //private
25645     setLastChild : function(node){
25646         this.lastChild = node;
25647     },
25648
25649
25650     /**
25651      * Returns true if this node is the last child of its parent
25652      * @return {Boolean}
25653      */
25654     isLast : function(){
25655        return (!this.parentNode ? true : this.parentNode.lastChild == this);
25656     },
25657
25658     /**
25659      * Returns true if this node is the first child of its parent
25660      * @return {Boolean}
25661      */
25662     isFirst : function(){
25663        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25664     },
25665
25666     hasChildNodes : function(){
25667         return !this.isLeaf() && this.childNodes.length > 0;
25668     },
25669
25670     /**
25671      * Insert node(s) as the last child node of this node.
25672      * @param {Node/Array} node The node or Array of nodes to append
25673      * @return {Node} The appended node if single append, or null if an array was passed
25674      */
25675     appendChild : function(node){
25676         var multi = false;
25677         if(node instanceof Array){
25678             multi = node;
25679         }else if(arguments.length > 1){
25680             multi = arguments;
25681         }
25682         
25683         // if passed an array or multiple args do them one by one
25684         if(multi){
25685             for(var i = 0, len = multi.length; i < len; i++) {
25686                 this.appendChild(multi[i]);
25687             }
25688         }else{
25689             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25690                 return false;
25691             }
25692             var index = this.childNodes.length;
25693             var oldParent = node.parentNode;
25694             // it's a move, make sure we move it cleanly
25695             if(oldParent){
25696                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25697                     return false;
25698                 }
25699                 oldParent.removeChild(node);
25700             }
25701             
25702             index = this.childNodes.length;
25703             if(index == 0){
25704                 this.setFirstChild(node);
25705             }
25706             this.childNodes.push(node);
25707             node.parentNode = this;
25708             var ps = this.childNodes[index-1];
25709             if(ps){
25710                 node.previousSibling = ps;
25711                 ps.nextSibling = node;
25712             }else{
25713                 node.previousSibling = null;
25714             }
25715             node.nextSibling = null;
25716             this.setLastChild(node);
25717             node.setOwnerTree(this.getOwnerTree());
25718             this.fireEvent("append", this.ownerTree, this, node, index);
25719             if(this.ownerTree) {
25720                 this.ownerTree.fireEvent("appendnode", this, node, index);
25721             }
25722             if(oldParent){
25723                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25724             }
25725             return node;
25726         }
25727     },
25728
25729     /**
25730      * Removes a child node from this node.
25731      * @param {Node} node The node to remove
25732      * @return {Node} The removed node
25733      */
25734     removeChild : function(node){
25735         var index = this.childNodes.indexOf(node);
25736         if(index == -1){
25737             return false;
25738         }
25739         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25740             return false;
25741         }
25742
25743         // remove it from childNodes collection
25744         this.childNodes.splice(index, 1);
25745
25746         // update siblings
25747         if(node.previousSibling){
25748             node.previousSibling.nextSibling = node.nextSibling;
25749         }
25750         if(node.nextSibling){
25751             node.nextSibling.previousSibling = node.previousSibling;
25752         }
25753
25754         // update child refs
25755         if(this.firstChild == node){
25756             this.setFirstChild(node.nextSibling);
25757         }
25758         if(this.lastChild == node){
25759             this.setLastChild(node.previousSibling);
25760         }
25761
25762         node.setOwnerTree(null);
25763         // clear any references from the node
25764         node.parentNode = null;
25765         node.previousSibling = null;
25766         node.nextSibling = null;
25767         this.fireEvent("remove", this.ownerTree, this, node);
25768         return node;
25769     },
25770
25771     /**
25772      * Inserts the first node before the second node in this nodes childNodes collection.
25773      * @param {Node} node The node to insert
25774      * @param {Node} refNode The node to insert before (if null the node is appended)
25775      * @return {Node} The inserted node
25776      */
25777     insertBefore : function(node, refNode){
25778         if(!refNode){ // like standard Dom, refNode can be null for append
25779             return this.appendChild(node);
25780         }
25781         // nothing to do
25782         if(node == refNode){
25783             return false;
25784         }
25785
25786         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25787             return false;
25788         }
25789         var index = this.childNodes.indexOf(refNode);
25790         var oldParent = node.parentNode;
25791         var refIndex = index;
25792
25793         // when moving internally, indexes will change after remove
25794         if(oldParent == this && this.childNodes.indexOf(node) < index){
25795             refIndex--;
25796         }
25797
25798         // it's a move, make sure we move it cleanly
25799         if(oldParent){
25800             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25801                 return false;
25802             }
25803             oldParent.removeChild(node);
25804         }
25805         if(refIndex == 0){
25806             this.setFirstChild(node);
25807         }
25808         this.childNodes.splice(refIndex, 0, node);
25809         node.parentNode = this;
25810         var ps = this.childNodes[refIndex-1];
25811         if(ps){
25812             node.previousSibling = ps;
25813             ps.nextSibling = node;
25814         }else{
25815             node.previousSibling = null;
25816         }
25817         node.nextSibling = refNode;
25818         refNode.previousSibling = node;
25819         node.setOwnerTree(this.getOwnerTree());
25820         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25821         if(oldParent){
25822             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25823         }
25824         return node;
25825     },
25826
25827     /**
25828      * Returns the child node at the specified index.
25829      * @param {Number} index
25830      * @return {Node}
25831      */
25832     item : function(index){
25833         return this.childNodes[index];
25834     },
25835
25836     /**
25837      * Replaces one child node in this node with another.
25838      * @param {Node} newChild The replacement node
25839      * @param {Node} oldChild The node to replace
25840      * @return {Node} The replaced node
25841      */
25842     replaceChild : function(newChild, oldChild){
25843         this.insertBefore(newChild, oldChild);
25844         this.removeChild(oldChild);
25845         return oldChild;
25846     },
25847
25848     /**
25849      * Returns the index of a child node
25850      * @param {Node} node
25851      * @return {Number} The index of the node or -1 if it was not found
25852      */
25853     indexOf : function(child){
25854         return this.childNodes.indexOf(child);
25855     },
25856
25857     /**
25858      * Returns the tree this node is in.
25859      * @return {Tree}
25860      */
25861     getOwnerTree : function(){
25862         // if it doesn't have one, look for one
25863         if(!this.ownerTree){
25864             var p = this;
25865             while(p){
25866                 if(p.ownerTree){
25867                     this.ownerTree = p.ownerTree;
25868                     break;
25869                 }
25870                 p = p.parentNode;
25871             }
25872         }
25873         return this.ownerTree;
25874     },
25875
25876     /**
25877      * Returns depth of this node (the root node has a depth of 0)
25878      * @return {Number}
25879      */
25880     getDepth : function(){
25881         var depth = 0;
25882         var p = this;
25883         while(p.parentNode){
25884             ++depth;
25885             p = p.parentNode;
25886         }
25887         return depth;
25888     },
25889
25890     // private
25891     setOwnerTree : function(tree){
25892         // if it's move, we need to update everyone
25893         if(tree != this.ownerTree){
25894             if(this.ownerTree){
25895                 this.ownerTree.unregisterNode(this);
25896             }
25897             this.ownerTree = tree;
25898             var cs = this.childNodes;
25899             for(var i = 0, len = cs.length; i < len; i++) {
25900                 cs[i].setOwnerTree(tree);
25901             }
25902             if(tree){
25903                 tree.registerNode(this);
25904             }
25905         }
25906     },
25907
25908     /**
25909      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25910      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25911      * @return {String} The path
25912      */
25913     getPath : function(attr){
25914         attr = attr || "id";
25915         var p = this.parentNode;
25916         var b = [this.attributes[attr]];
25917         while(p){
25918             b.unshift(p.attributes[attr]);
25919             p = p.parentNode;
25920         }
25921         var sep = this.getOwnerTree().pathSeparator;
25922         return sep + b.join(sep);
25923     },
25924
25925     /**
25926      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25927      * function call will be the scope provided or the current node. The arguments to the function
25928      * will be the args provided or the current node. If the function returns false at any point,
25929      * the bubble is stopped.
25930      * @param {Function} fn The function to call
25931      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25932      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25933      */
25934     bubble : function(fn, scope, args){
25935         var p = this;
25936         while(p){
25937             if(fn.call(scope || p, args || p) === false){
25938                 break;
25939             }
25940             p = p.parentNode;
25941         }
25942     },
25943
25944     /**
25945      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25946      * function call will be the scope provided or the current node. The arguments to the function
25947      * will be the args provided or the current node. If the function returns false at any point,
25948      * the cascade is stopped on that branch.
25949      * @param {Function} fn The function to call
25950      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25951      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25952      */
25953     cascade : function(fn, scope, args){
25954         if(fn.call(scope || this, args || this) !== false){
25955             var cs = this.childNodes;
25956             for(var i = 0, len = cs.length; i < len; i++) {
25957                 cs[i].cascade(fn, scope, args);
25958             }
25959         }
25960     },
25961
25962     /**
25963      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25964      * function call will be the scope provided or the current node. The arguments to the function
25965      * will be the args provided or the current node. If the function returns false at any point,
25966      * the iteration stops.
25967      * @param {Function} fn The function to call
25968      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25969      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25970      */
25971     eachChild : function(fn, scope, args){
25972         var cs = this.childNodes;
25973         for(var i = 0, len = cs.length; i < len; i++) {
25974                 if(fn.call(scope || this, args || cs[i]) === false){
25975                     break;
25976                 }
25977         }
25978     },
25979
25980     /**
25981      * Finds the first child that has the attribute with the specified value.
25982      * @param {String} attribute The attribute name
25983      * @param {Mixed} value The value to search for
25984      * @return {Node} The found child or null if none was found
25985      */
25986     findChild : function(attribute, value){
25987         var cs = this.childNodes;
25988         for(var i = 0, len = cs.length; i < len; i++) {
25989                 if(cs[i].attributes[attribute] == value){
25990                     return cs[i];
25991                 }
25992         }
25993         return null;
25994     },
25995
25996     /**
25997      * Finds the first child by a custom function. The child matches if the function passed
25998      * returns true.
25999      * @param {Function} fn
26000      * @param {Object} scope (optional)
26001      * @return {Node} The found child or null if none was found
26002      */
26003     findChildBy : function(fn, scope){
26004         var cs = this.childNodes;
26005         for(var i = 0, len = cs.length; i < len; i++) {
26006                 if(fn.call(scope||cs[i], cs[i]) === true){
26007                     return cs[i];
26008                 }
26009         }
26010         return null;
26011     },
26012
26013     /**
26014      * Sorts this nodes children using the supplied sort function
26015      * @param {Function} fn
26016      * @param {Object} scope (optional)
26017      */
26018     sort : function(fn, scope){
26019         var cs = this.childNodes;
26020         var len = cs.length;
26021         if(len > 0){
26022             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26023             cs.sort(sortFn);
26024             for(var i = 0; i < len; i++){
26025                 var n = cs[i];
26026                 n.previousSibling = cs[i-1];
26027                 n.nextSibling = cs[i+1];
26028                 if(i == 0){
26029                     this.setFirstChild(n);
26030                 }
26031                 if(i == len-1){
26032                     this.setLastChild(n);
26033                 }
26034             }
26035         }
26036     },
26037
26038     /**
26039      * Returns true if this node is an ancestor (at any point) of the passed node.
26040      * @param {Node} node
26041      * @return {Boolean}
26042      */
26043     contains : function(node){
26044         return node.isAncestor(this);
26045     },
26046
26047     /**
26048      * Returns true if the passed node is an ancestor (at any point) of this node.
26049      * @param {Node} node
26050      * @return {Boolean}
26051      */
26052     isAncestor : function(node){
26053         var p = this.parentNode;
26054         while(p){
26055             if(p == node){
26056                 return true;
26057             }
26058             p = p.parentNode;
26059         }
26060         return false;
26061     },
26062
26063     toString : function(){
26064         return "[Node"+(this.id?" "+this.id:"")+"]";
26065     }
26066 });/*
26067  * Based on:
26068  * Ext JS Library 1.1.1
26069  * Copyright(c) 2006-2007, Ext JS, LLC.
26070  *
26071  * Originally Released Under LGPL - original licence link has changed is not relivant.
26072  *
26073  * Fork - LGPL
26074  * <script type="text/javascript">
26075  */
26076
26077
26078 /**
26079  * @class Roo.Shadow
26080  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26081  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26082  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26083  * @constructor
26084  * Create a new Shadow
26085  * @param {Object} config The config object
26086  */
26087 Roo.Shadow = function(config){
26088     Roo.apply(this, config);
26089     if(typeof this.mode != "string"){
26090         this.mode = this.defaultMode;
26091     }
26092     var o = this.offset, a = {h: 0};
26093     var rad = Math.floor(this.offset/2);
26094     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26095         case "drop":
26096             a.w = 0;
26097             a.l = a.t = o;
26098             a.t -= 1;
26099             if(Roo.isIE){
26100                 a.l -= this.offset + rad;
26101                 a.t -= this.offset + rad;
26102                 a.w -= rad;
26103                 a.h -= rad;
26104                 a.t += 1;
26105             }
26106         break;
26107         case "sides":
26108             a.w = (o*2);
26109             a.l = -o;
26110             a.t = o-1;
26111             if(Roo.isIE){
26112                 a.l -= (this.offset - rad);
26113                 a.t -= this.offset + rad;
26114                 a.l += 1;
26115                 a.w -= (this.offset - rad)*2;
26116                 a.w -= rad + 1;
26117                 a.h -= 1;
26118             }
26119         break;
26120         case "frame":
26121             a.w = a.h = (o*2);
26122             a.l = a.t = -o;
26123             a.t += 1;
26124             a.h -= 2;
26125             if(Roo.isIE){
26126                 a.l -= (this.offset - rad);
26127                 a.t -= (this.offset - rad);
26128                 a.l += 1;
26129                 a.w -= (this.offset + rad + 1);
26130                 a.h -= (this.offset + rad);
26131                 a.h += 1;
26132             }
26133         break;
26134     };
26135
26136     this.adjusts = a;
26137 };
26138
26139 Roo.Shadow.prototype = {
26140     /**
26141      * @cfg {String} mode
26142      * The shadow display mode.  Supports the following options:<br />
26143      * sides: Shadow displays on both sides and bottom only<br />
26144      * frame: Shadow displays equally on all four sides<br />
26145      * drop: Traditional bottom-right drop shadow (default)
26146      */
26147     /**
26148      * @cfg {String} offset
26149      * The number of pixels to offset the shadow from the element (defaults to 4)
26150      */
26151     offset: 4,
26152
26153     // private
26154     defaultMode: "drop",
26155
26156     /**
26157      * Displays the shadow under the target element
26158      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26159      */
26160     show : function(target){
26161         target = Roo.get(target);
26162         if(!this.el){
26163             this.el = Roo.Shadow.Pool.pull();
26164             if(this.el.dom.nextSibling != target.dom){
26165                 this.el.insertBefore(target);
26166             }
26167         }
26168         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26169         if(Roo.isIE){
26170             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26171         }
26172         this.realign(
26173             target.getLeft(true),
26174             target.getTop(true),
26175             target.getWidth(),
26176             target.getHeight()
26177         );
26178         this.el.dom.style.display = "block";
26179     },
26180
26181     /**
26182      * Returns true if the shadow is visible, else false
26183      */
26184     isVisible : function(){
26185         return this.el ? true : false;  
26186     },
26187
26188     /**
26189      * Direct alignment when values are already available. Show must be called at least once before
26190      * calling this method to ensure it is initialized.
26191      * @param {Number} left The target element left position
26192      * @param {Number} top The target element top position
26193      * @param {Number} width The target element width
26194      * @param {Number} height The target element height
26195      */
26196     realign : function(l, t, w, h){
26197         if(!this.el){
26198             return;
26199         }
26200         var a = this.adjusts, d = this.el.dom, s = d.style;
26201         var iea = 0;
26202         s.left = (l+a.l)+"px";
26203         s.top = (t+a.t)+"px";
26204         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26205  
26206         if(s.width != sws || s.height != shs){
26207             s.width = sws;
26208             s.height = shs;
26209             if(!Roo.isIE){
26210                 var cn = d.childNodes;
26211                 var sww = Math.max(0, (sw-12))+"px";
26212                 cn[0].childNodes[1].style.width = sww;
26213                 cn[1].childNodes[1].style.width = sww;
26214                 cn[2].childNodes[1].style.width = sww;
26215                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26216             }
26217         }
26218     },
26219
26220     /**
26221      * Hides this shadow
26222      */
26223     hide : function(){
26224         if(this.el){
26225             this.el.dom.style.display = "none";
26226             Roo.Shadow.Pool.push(this.el);
26227             delete this.el;
26228         }
26229     },
26230
26231     /**
26232      * Adjust the z-index of this shadow
26233      * @param {Number} zindex The new z-index
26234      */
26235     setZIndex : function(z){
26236         this.zIndex = z;
26237         if(this.el){
26238             this.el.setStyle("z-index", z);
26239         }
26240     }
26241 };
26242
26243 // Private utility class that manages the internal Shadow cache
26244 Roo.Shadow.Pool = function(){
26245     var p = [];
26246     var markup = Roo.isIE ?
26247                  '<div class="x-ie-shadow"></div>' :
26248                  '<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>';
26249     return {
26250         pull : function(){
26251             var sh = p.shift();
26252             if(!sh){
26253                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26254                 sh.autoBoxAdjust = false;
26255             }
26256             return sh;
26257         },
26258
26259         push : function(sh){
26260             p.push(sh);
26261         }
26262     };
26263 }();/*
26264  * Based on:
26265  * Ext JS Library 1.1.1
26266  * Copyright(c) 2006-2007, Ext JS, LLC.
26267  *
26268  * Originally Released Under LGPL - original licence link has changed is not relivant.
26269  *
26270  * Fork - LGPL
26271  * <script type="text/javascript">
26272  */
26273
26274
26275 /**
26276  * @class Roo.SplitBar
26277  * @extends Roo.util.Observable
26278  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26279  * <br><br>
26280  * Usage:
26281  * <pre><code>
26282 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26283                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26284 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26285 split.minSize = 100;
26286 split.maxSize = 600;
26287 split.animate = true;
26288 split.on('moved', splitterMoved);
26289 </code></pre>
26290  * @constructor
26291  * Create a new SplitBar
26292  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26293  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26294  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26295  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26296                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26297                         position of the SplitBar).
26298  */
26299 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26300     
26301     /** @private */
26302     this.el = Roo.get(dragElement, true);
26303     this.el.dom.unselectable = "on";
26304     /** @private */
26305     this.resizingEl = Roo.get(resizingElement, true);
26306
26307     /**
26308      * @private
26309      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26310      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26311      * @type Number
26312      */
26313     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26314     
26315     /**
26316      * The minimum size of the resizing element. (Defaults to 0)
26317      * @type Number
26318      */
26319     this.minSize = 0;
26320     
26321     /**
26322      * The maximum size of the resizing element. (Defaults to 2000)
26323      * @type Number
26324      */
26325     this.maxSize = 2000;
26326     
26327     /**
26328      * Whether to animate the transition to the new size
26329      * @type Boolean
26330      */
26331     this.animate = false;
26332     
26333     /**
26334      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26335      * @type Boolean
26336      */
26337     this.useShim = false;
26338     
26339     /** @private */
26340     this.shim = null;
26341     
26342     if(!existingProxy){
26343         /** @private */
26344         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26345     }else{
26346         this.proxy = Roo.get(existingProxy).dom;
26347     }
26348     /** @private */
26349     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26350     
26351     /** @private */
26352     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26353     
26354     /** @private */
26355     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26356     
26357     /** @private */
26358     this.dragSpecs = {};
26359     
26360     /**
26361      * @private The adapter to use to positon and resize elements
26362      */
26363     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26364     this.adapter.init(this);
26365     
26366     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26367         /** @private */
26368         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26369         this.el.addClass("x-splitbar-h");
26370     }else{
26371         /** @private */
26372         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26373         this.el.addClass("x-splitbar-v");
26374     }
26375     
26376     this.addEvents({
26377         /**
26378          * @event resize
26379          * Fires when the splitter is moved (alias for {@link #event-moved})
26380          * @param {Roo.SplitBar} this
26381          * @param {Number} newSize the new width or height
26382          */
26383         "resize" : true,
26384         /**
26385          * @event moved
26386          * Fires when the splitter is moved
26387          * @param {Roo.SplitBar} this
26388          * @param {Number} newSize the new width or height
26389          */
26390         "moved" : true,
26391         /**
26392          * @event beforeresize
26393          * Fires before the splitter is dragged
26394          * @param {Roo.SplitBar} this
26395          */
26396         "beforeresize" : true,
26397
26398         "beforeapply" : true
26399     });
26400
26401     Roo.util.Observable.call(this);
26402 };
26403
26404 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26405     onStartProxyDrag : function(x, y){
26406         this.fireEvent("beforeresize", this);
26407         if(!this.overlay){
26408             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26409             o.unselectable();
26410             o.enableDisplayMode("block");
26411             // all splitbars share the same overlay
26412             Roo.SplitBar.prototype.overlay = o;
26413         }
26414         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26415         this.overlay.show();
26416         Roo.get(this.proxy).setDisplayed("block");
26417         var size = this.adapter.getElementSize(this);
26418         this.activeMinSize = this.getMinimumSize();;
26419         this.activeMaxSize = this.getMaximumSize();;
26420         var c1 = size - this.activeMinSize;
26421         var c2 = Math.max(this.activeMaxSize - size, 0);
26422         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26423             this.dd.resetConstraints();
26424             this.dd.setXConstraint(
26425                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26426                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26427             );
26428             this.dd.setYConstraint(0, 0);
26429         }else{
26430             this.dd.resetConstraints();
26431             this.dd.setXConstraint(0, 0);
26432             this.dd.setYConstraint(
26433                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26434                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26435             );
26436          }
26437         this.dragSpecs.startSize = size;
26438         this.dragSpecs.startPoint = [x, y];
26439         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26440     },
26441     
26442     /** 
26443      * @private Called after the drag operation by the DDProxy
26444      */
26445     onEndProxyDrag : function(e){
26446         Roo.get(this.proxy).setDisplayed(false);
26447         var endPoint = Roo.lib.Event.getXY(e);
26448         if(this.overlay){
26449             this.overlay.hide();
26450         }
26451         var newSize;
26452         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26453             newSize = this.dragSpecs.startSize + 
26454                 (this.placement == Roo.SplitBar.LEFT ?
26455                     endPoint[0] - this.dragSpecs.startPoint[0] :
26456                     this.dragSpecs.startPoint[0] - endPoint[0]
26457                 );
26458         }else{
26459             newSize = this.dragSpecs.startSize + 
26460                 (this.placement == Roo.SplitBar.TOP ?
26461                     endPoint[1] - this.dragSpecs.startPoint[1] :
26462                     this.dragSpecs.startPoint[1] - endPoint[1]
26463                 );
26464         }
26465         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26466         if(newSize != this.dragSpecs.startSize){
26467             if(this.fireEvent('beforeapply', this, newSize) !== false){
26468                 this.adapter.setElementSize(this, newSize);
26469                 this.fireEvent("moved", this, newSize);
26470                 this.fireEvent("resize", this, newSize);
26471             }
26472         }
26473     },
26474     
26475     /**
26476      * Get the adapter this SplitBar uses
26477      * @return The adapter object
26478      */
26479     getAdapter : function(){
26480         return this.adapter;
26481     },
26482     
26483     /**
26484      * Set the adapter this SplitBar uses
26485      * @param {Object} adapter A SplitBar adapter object
26486      */
26487     setAdapter : function(adapter){
26488         this.adapter = adapter;
26489         this.adapter.init(this);
26490     },
26491     
26492     /**
26493      * Gets the minimum size for the resizing element
26494      * @return {Number} The minimum size
26495      */
26496     getMinimumSize : function(){
26497         return this.minSize;
26498     },
26499     
26500     /**
26501      * Sets the minimum size for the resizing element
26502      * @param {Number} minSize The minimum size
26503      */
26504     setMinimumSize : function(minSize){
26505         this.minSize = minSize;
26506     },
26507     
26508     /**
26509      * Gets the maximum size for the resizing element
26510      * @return {Number} The maximum size
26511      */
26512     getMaximumSize : function(){
26513         return this.maxSize;
26514     },
26515     
26516     /**
26517      * Sets the maximum size for the resizing element
26518      * @param {Number} maxSize The maximum size
26519      */
26520     setMaximumSize : function(maxSize){
26521         this.maxSize = maxSize;
26522     },
26523     
26524     /**
26525      * Sets the initialize size for the resizing element
26526      * @param {Number} size The initial size
26527      */
26528     setCurrentSize : function(size){
26529         var oldAnimate = this.animate;
26530         this.animate = false;
26531         this.adapter.setElementSize(this, size);
26532         this.animate = oldAnimate;
26533     },
26534     
26535     /**
26536      * Destroy this splitbar. 
26537      * @param {Boolean} removeEl True to remove the element
26538      */
26539     destroy : function(removeEl){
26540         if(this.shim){
26541             this.shim.remove();
26542         }
26543         this.dd.unreg();
26544         this.proxy.parentNode.removeChild(this.proxy);
26545         if(removeEl){
26546             this.el.remove();
26547         }
26548     }
26549 });
26550
26551 /**
26552  * @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.
26553  */
26554 Roo.SplitBar.createProxy = function(dir){
26555     var proxy = new Roo.Element(document.createElement("div"));
26556     proxy.unselectable();
26557     var cls = 'x-splitbar-proxy';
26558     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26559     document.body.appendChild(proxy.dom);
26560     return proxy.dom;
26561 };
26562
26563 /** 
26564  * @class Roo.SplitBar.BasicLayoutAdapter
26565  * Default Adapter. It assumes the splitter and resizing element are not positioned
26566  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26567  */
26568 Roo.SplitBar.BasicLayoutAdapter = function(){
26569 };
26570
26571 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26572     // do nothing for now
26573     init : function(s){
26574     
26575     },
26576     /**
26577      * Called before drag operations to get the current size of the resizing element. 
26578      * @param {Roo.SplitBar} s The SplitBar using this adapter
26579      */
26580      getElementSize : function(s){
26581         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26582             return s.resizingEl.getWidth();
26583         }else{
26584             return s.resizingEl.getHeight();
26585         }
26586     },
26587     
26588     /**
26589      * Called after drag operations to set the size of the resizing element.
26590      * @param {Roo.SplitBar} s The SplitBar using this adapter
26591      * @param {Number} newSize The new size to set
26592      * @param {Function} onComplete A function to be invoked when resizing is complete
26593      */
26594     setElementSize : function(s, newSize, onComplete){
26595         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26596             if(!s.animate){
26597                 s.resizingEl.setWidth(newSize);
26598                 if(onComplete){
26599                     onComplete(s, newSize);
26600                 }
26601             }else{
26602                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26603             }
26604         }else{
26605             
26606             if(!s.animate){
26607                 s.resizingEl.setHeight(newSize);
26608                 if(onComplete){
26609                     onComplete(s, newSize);
26610                 }
26611             }else{
26612                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26613             }
26614         }
26615     }
26616 };
26617
26618 /** 
26619  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26620  * @extends Roo.SplitBar.BasicLayoutAdapter
26621  * Adapter that  moves the splitter element to align with the resized sizing element. 
26622  * Used with an absolute positioned SplitBar.
26623  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26624  * document.body, make sure you assign an id to the body element.
26625  */
26626 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26627     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26628     this.container = Roo.get(container);
26629 };
26630
26631 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26632     init : function(s){
26633         this.basic.init(s);
26634     },
26635     
26636     getElementSize : function(s){
26637         return this.basic.getElementSize(s);
26638     },
26639     
26640     setElementSize : function(s, newSize, onComplete){
26641         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26642     },
26643     
26644     moveSplitter : function(s){
26645         var yes = Roo.SplitBar;
26646         switch(s.placement){
26647             case yes.LEFT:
26648                 s.el.setX(s.resizingEl.getRight());
26649                 break;
26650             case yes.RIGHT:
26651                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26652                 break;
26653             case yes.TOP:
26654                 s.el.setY(s.resizingEl.getBottom());
26655                 break;
26656             case yes.BOTTOM:
26657                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26658                 break;
26659         }
26660     }
26661 };
26662
26663 /**
26664  * Orientation constant - Create a vertical SplitBar
26665  * @static
26666  * @type Number
26667  */
26668 Roo.SplitBar.VERTICAL = 1;
26669
26670 /**
26671  * Orientation constant - Create a horizontal SplitBar
26672  * @static
26673  * @type Number
26674  */
26675 Roo.SplitBar.HORIZONTAL = 2;
26676
26677 /**
26678  * Placement constant - The resizing element is to the left of the splitter element
26679  * @static
26680  * @type Number
26681  */
26682 Roo.SplitBar.LEFT = 1;
26683
26684 /**
26685  * Placement constant - The resizing element is to the right of the splitter element
26686  * @static
26687  * @type Number
26688  */
26689 Roo.SplitBar.RIGHT = 2;
26690
26691 /**
26692  * Placement constant - The resizing element is positioned above the splitter element
26693  * @static
26694  * @type Number
26695  */
26696 Roo.SplitBar.TOP = 3;
26697
26698 /**
26699  * Placement constant - The resizing element is positioned under splitter element
26700  * @static
26701  * @type Number
26702  */
26703 Roo.SplitBar.BOTTOM = 4;
26704 /*
26705  * Based on:
26706  * Ext JS Library 1.1.1
26707  * Copyright(c) 2006-2007, Ext JS, LLC.
26708  *
26709  * Originally Released Under LGPL - original licence link has changed is not relivant.
26710  *
26711  * Fork - LGPL
26712  * <script type="text/javascript">
26713  */
26714
26715 /**
26716  * @class Roo.View
26717  * @extends Roo.util.Observable
26718  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26719  * This class also supports single and multi selection modes. <br>
26720  * Create a data model bound view:
26721  <pre><code>
26722  var store = new Roo.data.Store(...);
26723
26724  var view = new Roo.View({
26725     el : "my-element",
26726     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26727  
26728     singleSelect: true,
26729     selectedClass: "ydataview-selected",
26730     store: store
26731  });
26732
26733  // listen for node click?
26734  view.on("click", function(vw, index, node, e){
26735  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26736  });
26737
26738  // load XML data
26739  dataModel.load("foobar.xml");
26740  </code></pre>
26741  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26742  * <br><br>
26743  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26744  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26745  * 
26746  * Note: old style constructor is still suported (container, template, config)
26747  * 
26748  * @constructor
26749  * Create a new View
26750  * @param {Object} config The config object
26751  * 
26752  */
26753 Roo.View = function(config, depreciated_tpl, depreciated_config){
26754     
26755     this.parent = false;
26756     
26757     if (typeof(depreciated_tpl) == 'undefined') {
26758         // new way.. - universal constructor.
26759         Roo.apply(this, config);
26760         this.el  = Roo.get(this.el);
26761     } else {
26762         // old format..
26763         this.el  = Roo.get(config);
26764         this.tpl = depreciated_tpl;
26765         Roo.apply(this, depreciated_config);
26766     }
26767     this.wrapEl  = this.el.wrap().wrap();
26768     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26769     
26770     
26771     if(typeof(this.tpl) == "string"){
26772         this.tpl = new Roo.Template(this.tpl);
26773     } else {
26774         // support xtype ctors..
26775         this.tpl = new Roo.factory(this.tpl, Roo);
26776     }
26777     
26778     
26779     this.tpl.compile();
26780     
26781     /** @private */
26782     this.addEvents({
26783         /**
26784          * @event beforeclick
26785          * Fires before a click is processed. Returns false to cancel the default action.
26786          * @param {Roo.View} this
26787          * @param {Number} index The index of the target node
26788          * @param {HTMLElement} node The target node
26789          * @param {Roo.EventObject} e The raw event object
26790          */
26791             "beforeclick" : true,
26792         /**
26793          * @event click
26794          * Fires when a template node is clicked.
26795          * @param {Roo.View} this
26796          * @param {Number} index The index of the target node
26797          * @param {HTMLElement} node The target node
26798          * @param {Roo.EventObject} e The raw event object
26799          */
26800             "click" : true,
26801         /**
26802          * @event dblclick
26803          * Fires when a template node is double clicked.
26804          * @param {Roo.View} this
26805          * @param {Number} index The index of the target node
26806          * @param {HTMLElement} node The target node
26807          * @param {Roo.EventObject} e The raw event object
26808          */
26809             "dblclick" : true,
26810         /**
26811          * @event contextmenu
26812          * Fires when a template node is right clicked.
26813          * @param {Roo.View} this
26814          * @param {Number} index The index of the target node
26815          * @param {HTMLElement} node The target node
26816          * @param {Roo.EventObject} e The raw event object
26817          */
26818             "contextmenu" : true,
26819         /**
26820          * @event selectionchange
26821          * Fires when the selected nodes change.
26822          * @param {Roo.View} this
26823          * @param {Array} selections Array of the selected nodes
26824          */
26825             "selectionchange" : true,
26826     
26827         /**
26828          * @event beforeselect
26829          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26830          * @param {Roo.View} this
26831          * @param {HTMLElement} node The node to be selected
26832          * @param {Array} selections Array of currently selected nodes
26833          */
26834             "beforeselect" : true,
26835         /**
26836          * @event preparedata
26837          * Fires on every row to render, to allow you to change the data.
26838          * @param {Roo.View} this
26839          * @param {Object} data to be rendered (change this)
26840          */
26841           "preparedata" : true
26842           
26843           
26844         });
26845
26846
26847
26848     this.el.on({
26849         "click": this.onClick,
26850         "dblclick": this.onDblClick,
26851         "contextmenu": this.onContextMenu,
26852         scope:this
26853     });
26854
26855     this.selections = [];
26856     this.nodes = [];
26857     this.cmp = new Roo.CompositeElementLite([]);
26858     if(this.store){
26859         this.store = Roo.factory(this.store, Roo.data);
26860         this.setStore(this.store, true);
26861     }
26862     
26863     if ( this.footer && this.footer.xtype) {
26864            
26865          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26866         
26867         this.footer.dataSource = this.store;
26868         this.footer.container = fctr;
26869         this.footer = Roo.factory(this.footer, Roo);
26870         fctr.insertFirst(this.el);
26871         
26872         // this is a bit insane - as the paging toolbar seems to detach the el..
26873 //        dom.parentNode.parentNode.parentNode
26874          // they get detached?
26875     }
26876     
26877     
26878     Roo.View.superclass.constructor.call(this);
26879     
26880     
26881 };
26882
26883 Roo.extend(Roo.View, Roo.util.Observable, {
26884     
26885      /**
26886      * @cfg {Roo.data.Store} store Data store to load data from.
26887      */
26888     store : false,
26889     
26890     /**
26891      * @cfg {String|Roo.Element} el The container element.
26892      */
26893     el : '',
26894     
26895     /**
26896      * @cfg {String|Roo.Template} tpl The template used by this View 
26897      */
26898     tpl : false,
26899     /**
26900      * @cfg {String} dataName the named area of the template to use as the data area
26901      *                          Works with domtemplates roo-name="name"
26902      */
26903     dataName: false,
26904     /**
26905      * @cfg {String} selectedClass The css class to add to selected nodes
26906      */
26907     selectedClass : "x-view-selected",
26908      /**
26909      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26910      */
26911     emptyText : "",
26912     
26913     /**
26914      * @cfg {String} text to display on mask (default Loading)
26915      */
26916     mask : false,
26917     /**
26918      * @cfg {Boolean} multiSelect Allow multiple selection
26919      */
26920     multiSelect : false,
26921     /**
26922      * @cfg {Boolean} singleSelect Allow single selection
26923      */
26924     singleSelect:  false,
26925     
26926     /**
26927      * @cfg {Boolean} toggleSelect - selecting 
26928      */
26929     toggleSelect : false,
26930     
26931     /**
26932      * @cfg {Boolean} tickable - selecting 
26933      */
26934     tickable : false,
26935     
26936     /**
26937      * Returns the element this view is bound to.
26938      * @return {Roo.Element}
26939      */
26940     getEl : function(){
26941         return this.wrapEl;
26942     },
26943     
26944     
26945
26946     /**
26947      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26948      */
26949     refresh : function(){
26950         //Roo.log('refresh');
26951         var t = this.tpl;
26952         
26953         // if we are using something like 'domtemplate', then
26954         // the what gets used is:
26955         // t.applySubtemplate(NAME, data, wrapping data..)
26956         // the outer template then get' applied with
26957         //     the store 'extra data'
26958         // and the body get's added to the
26959         //      roo-name="data" node?
26960         //      <span class='roo-tpl-{name}'></span> ?????
26961         
26962         
26963         
26964         this.clearSelections();
26965         this.el.update("");
26966         var html = [];
26967         var records = this.store.getRange();
26968         if(records.length < 1) {
26969             
26970             // is this valid??  = should it render a template??
26971             
26972             this.el.update(this.emptyText);
26973             return;
26974         }
26975         var el = this.el;
26976         if (this.dataName) {
26977             this.el.update(t.apply(this.store.meta)); //????
26978             el = this.el.child('.roo-tpl-' + this.dataName);
26979         }
26980         
26981         for(var i = 0, len = records.length; i < len; i++){
26982             var data = this.prepareData(records[i].data, i, records[i]);
26983             this.fireEvent("preparedata", this, data, i, records[i]);
26984             
26985             var d = Roo.apply({}, data);
26986             
26987             if(this.tickable){
26988                 Roo.apply(d, {'roo-id' : Roo.id()});
26989                 
26990                 var _this = this;
26991             
26992                 Roo.each(this.parent.item, function(item){
26993                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26994                         return;
26995                     }
26996                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26997                 });
26998             }
26999             
27000             html[html.length] = Roo.util.Format.trim(
27001                 this.dataName ?
27002                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27003                     t.apply(d)
27004             );
27005         }
27006         
27007         
27008         
27009         el.update(html.join(""));
27010         this.nodes = el.dom.childNodes;
27011         this.updateIndexes(0);
27012     },
27013     
27014
27015     /**
27016      * Function to override to reformat the data that is sent to
27017      * the template for each node.
27018      * DEPRICATED - use the preparedata event handler.
27019      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27020      * a JSON object for an UpdateManager bound view).
27021      */
27022     prepareData : function(data, index, record)
27023     {
27024         this.fireEvent("preparedata", this, data, index, record);
27025         return data;
27026     },
27027
27028     onUpdate : function(ds, record){
27029         // Roo.log('on update');   
27030         this.clearSelections();
27031         var index = this.store.indexOf(record);
27032         var n = this.nodes[index];
27033         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27034         n.parentNode.removeChild(n);
27035         this.updateIndexes(index, index);
27036     },
27037
27038     
27039     
27040 // --------- FIXME     
27041     onAdd : function(ds, records, index)
27042     {
27043         //Roo.log(['on Add', ds, records, index] );        
27044         this.clearSelections();
27045         if(this.nodes.length == 0){
27046             this.refresh();
27047             return;
27048         }
27049         var n = this.nodes[index];
27050         for(var i = 0, len = records.length; i < len; i++){
27051             var d = this.prepareData(records[i].data, i, records[i]);
27052             if(n){
27053                 this.tpl.insertBefore(n, d);
27054             }else{
27055                 
27056                 this.tpl.append(this.el, d);
27057             }
27058         }
27059         this.updateIndexes(index);
27060     },
27061
27062     onRemove : function(ds, record, index){
27063        // Roo.log('onRemove');
27064         this.clearSelections();
27065         var el = this.dataName  ?
27066             this.el.child('.roo-tpl-' + this.dataName) :
27067             this.el; 
27068         
27069         el.dom.removeChild(this.nodes[index]);
27070         this.updateIndexes(index);
27071     },
27072
27073     /**
27074      * Refresh an individual node.
27075      * @param {Number} index
27076      */
27077     refreshNode : function(index){
27078         this.onUpdate(this.store, this.store.getAt(index));
27079     },
27080
27081     updateIndexes : function(startIndex, endIndex){
27082         var ns = this.nodes;
27083         startIndex = startIndex || 0;
27084         endIndex = endIndex || ns.length - 1;
27085         for(var i = startIndex; i <= endIndex; i++){
27086             ns[i].nodeIndex = i;
27087         }
27088     },
27089
27090     /**
27091      * Changes the data store this view uses and refresh the view.
27092      * @param {Store} store
27093      */
27094     setStore : function(store, initial){
27095         if(!initial && this.store){
27096             this.store.un("datachanged", this.refresh);
27097             this.store.un("add", this.onAdd);
27098             this.store.un("remove", this.onRemove);
27099             this.store.un("update", this.onUpdate);
27100             this.store.un("clear", this.refresh);
27101             this.store.un("beforeload", this.onBeforeLoad);
27102             this.store.un("load", this.onLoad);
27103             this.store.un("loadexception", this.onLoad);
27104         }
27105         if(store){
27106           
27107             store.on("datachanged", this.refresh, this);
27108             store.on("add", this.onAdd, this);
27109             store.on("remove", this.onRemove, this);
27110             store.on("update", this.onUpdate, this);
27111             store.on("clear", this.refresh, this);
27112             store.on("beforeload", this.onBeforeLoad, this);
27113             store.on("load", this.onLoad, this);
27114             store.on("loadexception", this.onLoad, this);
27115         }
27116         
27117         if(store){
27118             this.refresh();
27119         }
27120     },
27121     /**
27122      * onbeforeLoad - masks the loading area.
27123      *
27124      */
27125     onBeforeLoad : function(store,opts)
27126     {
27127          //Roo.log('onBeforeLoad');   
27128         if (!opts.add) {
27129             this.el.update("");
27130         }
27131         this.el.mask(this.mask ? this.mask : "Loading" ); 
27132     },
27133     onLoad : function ()
27134     {
27135         this.el.unmask();
27136     },
27137     
27138
27139     /**
27140      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27141      * @param {HTMLElement} node
27142      * @return {HTMLElement} The template node
27143      */
27144     findItemFromChild : function(node){
27145         var el = this.dataName  ?
27146             this.el.child('.roo-tpl-' + this.dataName,true) :
27147             this.el.dom; 
27148         
27149         if(!node || node.parentNode == el){
27150                     return node;
27151             }
27152             var p = node.parentNode;
27153             while(p && p != el){
27154             if(p.parentNode == el){
27155                 return p;
27156             }
27157             p = p.parentNode;
27158         }
27159             return null;
27160     },
27161
27162     /** @ignore */
27163     onClick : function(e){
27164         var item = this.findItemFromChild(e.getTarget());
27165         if(item){
27166             var index = this.indexOf(item);
27167             if(this.onItemClick(item, index, e) !== false){
27168                 this.fireEvent("click", this, index, item, e);
27169             }
27170         }else{
27171             this.clearSelections();
27172         }
27173     },
27174
27175     /** @ignore */
27176     onContextMenu : function(e){
27177         var item = this.findItemFromChild(e.getTarget());
27178         if(item){
27179             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27180         }
27181     },
27182
27183     /** @ignore */
27184     onDblClick : function(e){
27185         var item = this.findItemFromChild(e.getTarget());
27186         if(item){
27187             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27188         }
27189     },
27190
27191     onItemClick : function(item, index, e)
27192     {
27193         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27194             return false;
27195         }
27196         if (this.toggleSelect) {
27197             var m = this.isSelected(item) ? 'unselect' : 'select';
27198             //Roo.log(m);
27199             var _t = this;
27200             _t[m](item, true, false);
27201             return true;
27202         }
27203         if(this.multiSelect || this.singleSelect){
27204             if(this.multiSelect && e.shiftKey && this.lastSelection){
27205                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27206             }else{
27207                 this.select(item, this.multiSelect && e.ctrlKey);
27208                 this.lastSelection = item;
27209             }
27210             
27211             if(!this.tickable){
27212                 e.preventDefault();
27213             }
27214             
27215         }
27216         return true;
27217     },
27218
27219     /**
27220      * Get the number of selected nodes.
27221      * @return {Number}
27222      */
27223     getSelectionCount : function(){
27224         return this.selections.length;
27225     },
27226
27227     /**
27228      * Get the currently selected nodes.
27229      * @return {Array} An array of HTMLElements
27230      */
27231     getSelectedNodes : function(){
27232         return this.selections;
27233     },
27234
27235     /**
27236      * Get the indexes of the selected nodes.
27237      * @return {Array}
27238      */
27239     getSelectedIndexes : function(){
27240         var indexes = [], s = this.selections;
27241         for(var i = 0, len = s.length; i < len; i++){
27242             indexes.push(s[i].nodeIndex);
27243         }
27244         return indexes;
27245     },
27246
27247     /**
27248      * Clear all selections
27249      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27250      */
27251     clearSelections : function(suppressEvent){
27252         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27253             this.cmp.elements = this.selections;
27254             this.cmp.removeClass(this.selectedClass);
27255             this.selections = [];
27256             if(!suppressEvent){
27257                 this.fireEvent("selectionchange", this, this.selections);
27258             }
27259         }
27260     },
27261
27262     /**
27263      * Returns true if the passed node is selected
27264      * @param {HTMLElement/Number} node The node or node index
27265      * @return {Boolean}
27266      */
27267     isSelected : function(node){
27268         var s = this.selections;
27269         if(s.length < 1){
27270             return false;
27271         }
27272         node = this.getNode(node);
27273         return s.indexOf(node) !== -1;
27274     },
27275
27276     /**
27277      * Selects nodes.
27278      * @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
27279      * @param {Boolean} keepExisting (optional) true to keep existing selections
27280      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27281      */
27282     select : function(nodeInfo, keepExisting, suppressEvent){
27283         if(nodeInfo instanceof Array){
27284             if(!keepExisting){
27285                 this.clearSelections(true);
27286             }
27287             for(var i = 0, len = nodeInfo.length; i < len; i++){
27288                 this.select(nodeInfo[i], true, true);
27289             }
27290             return;
27291         } 
27292         var node = this.getNode(nodeInfo);
27293         if(!node || this.isSelected(node)){
27294             return; // already selected.
27295         }
27296         if(!keepExisting){
27297             this.clearSelections(true);
27298         }
27299         
27300         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27301             Roo.fly(node).addClass(this.selectedClass);
27302             this.selections.push(node);
27303             if(!suppressEvent){
27304                 this.fireEvent("selectionchange", this, this.selections);
27305             }
27306         }
27307         
27308         
27309     },
27310       /**
27311      * Unselects nodes.
27312      * @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
27313      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27314      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27315      */
27316     unselect : function(nodeInfo, keepExisting, suppressEvent)
27317     {
27318         if(nodeInfo instanceof Array){
27319             Roo.each(this.selections, function(s) {
27320                 this.unselect(s, nodeInfo);
27321             }, this);
27322             return;
27323         }
27324         var node = this.getNode(nodeInfo);
27325         if(!node || !this.isSelected(node)){
27326             //Roo.log("not selected");
27327             return; // not selected.
27328         }
27329         // fireevent???
27330         var ns = [];
27331         Roo.each(this.selections, function(s) {
27332             if (s == node ) {
27333                 Roo.fly(node).removeClass(this.selectedClass);
27334
27335                 return;
27336             }
27337             ns.push(s);
27338         },this);
27339         
27340         this.selections= ns;
27341         this.fireEvent("selectionchange", this, this.selections);
27342     },
27343
27344     /**
27345      * Gets a template node.
27346      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27347      * @return {HTMLElement} The node or null if it wasn't found
27348      */
27349     getNode : function(nodeInfo){
27350         if(typeof nodeInfo == "string"){
27351             return document.getElementById(nodeInfo);
27352         }else if(typeof nodeInfo == "number"){
27353             return this.nodes[nodeInfo];
27354         }
27355         return nodeInfo;
27356     },
27357
27358     /**
27359      * Gets a range template nodes.
27360      * @param {Number} startIndex
27361      * @param {Number} endIndex
27362      * @return {Array} An array of nodes
27363      */
27364     getNodes : function(start, end){
27365         var ns = this.nodes;
27366         start = start || 0;
27367         end = typeof end == "undefined" ? ns.length - 1 : end;
27368         var nodes = [];
27369         if(start <= end){
27370             for(var i = start; i <= end; i++){
27371                 nodes.push(ns[i]);
27372             }
27373         } else{
27374             for(var i = start; i >= end; i--){
27375                 nodes.push(ns[i]);
27376             }
27377         }
27378         return nodes;
27379     },
27380
27381     /**
27382      * Finds the index of the passed node
27383      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27384      * @return {Number} The index of the node or -1
27385      */
27386     indexOf : function(node){
27387         node = this.getNode(node);
27388         if(typeof node.nodeIndex == "number"){
27389             return node.nodeIndex;
27390         }
27391         var ns = this.nodes;
27392         for(var i = 0, len = ns.length; i < len; i++){
27393             if(ns[i] == node){
27394                 return i;
27395             }
27396         }
27397         return -1;
27398     }
27399 });
27400 /*
27401  * Based on:
27402  * Ext JS Library 1.1.1
27403  * Copyright(c) 2006-2007, Ext JS, LLC.
27404  *
27405  * Originally Released Under LGPL - original licence link has changed is not relivant.
27406  *
27407  * Fork - LGPL
27408  * <script type="text/javascript">
27409  */
27410
27411 /**
27412  * @class Roo.JsonView
27413  * @extends Roo.View
27414  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27415 <pre><code>
27416 var view = new Roo.JsonView({
27417     container: "my-element",
27418     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27419     multiSelect: true, 
27420     jsonRoot: "data" 
27421 });
27422
27423 // listen for node click?
27424 view.on("click", function(vw, index, node, e){
27425     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27426 });
27427
27428 // direct load of JSON data
27429 view.load("foobar.php");
27430
27431 // Example from my blog list
27432 var tpl = new Roo.Template(
27433     '&lt;div class="entry"&gt;' +
27434     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27435     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27436     "&lt;/div&gt;&lt;hr /&gt;"
27437 );
27438
27439 var moreView = new Roo.JsonView({
27440     container :  "entry-list", 
27441     template : tpl,
27442     jsonRoot: "posts"
27443 });
27444 moreView.on("beforerender", this.sortEntries, this);
27445 moreView.load({
27446     url: "/blog/get-posts.php",
27447     params: "allposts=true",
27448     text: "Loading Blog Entries..."
27449 });
27450 </code></pre>
27451
27452 * Note: old code is supported with arguments : (container, template, config)
27453
27454
27455  * @constructor
27456  * Create a new JsonView
27457  * 
27458  * @param {Object} config The config object
27459  * 
27460  */
27461 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27462     
27463     
27464     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27465
27466     var um = this.el.getUpdateManager();
27467     um.setRenderer(this);
27468     um.on("update", this.onLoad, this);
27469     um.on("failure", this.onLoadException, this);
27470
27471     /**
27472      * @event beforerender
27473      * Fires before rendering of the downloaded JSON data.
27474      * @param {Roo.JsonView} this
27475      * @param {Object} data The JSON data loaded
27476      */
27477     /**
27478      * @event load
27479      * Fires when data is loaded.
27480      * @param {Roo.JsonView} this
27481      * @param {Object} data The JSON data loaded
27482      * @param {Object} response The raw Connect response object
27483      */
27484     /**
27485      * @event loadexception
27486      * Fires when loading fails.
27487      * @param {Roo.JsonView} this
27488      * @param {Object} response The raw Connect response object
27489      */
27490     this.addEvents({
27491         'beforerender' : true,
27492         'load' : true,
27493         'loadexception' : true
27494     });
27495 };
27496 Roo.extend(Roo.JsonView, Roo.View, {
27497     /**
27498      * @type {String} The root property in the loaded JSON object that contains the data
27499      */
27500     jsonRoot : "",
27501
27502     /**
27503      * Refreshes the view.
27504      */
27505     refresh : function(){
27506         this.clearSelections();
27507         this.el.update("");
27508         var html = [];
27509         var o = this.jsonData;
27510         if(o && o.length > 0){
27511             for(var i = 0, len = o.length; i < len; i++){
27512                 var data = this.prepareData(o[i], i, o);
27513                 html[html.length] = this.tpl.apply(data);
27514             }
27515         }else{
27516             html.push(this.emptyText);
27517         }
27518         this.el.update(html.join(""));
27519         this.nodes = this.el.dom.childNodes;
27520         this.updateIndexes(0);
27521     },
27522
27523     /**
27524      * 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.
27525      * @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:
27526      <pre><code>
27527      view.load({
27528          url: "your-url.php",
27529          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27530          callback: yourFunction,
27531          scope: yourObject, //(optional scope)
27532          discardUrl: false,
27533          nocache: false,
27534          text: "Loading...",
27535          timeout: 30,
27536          scripts: false
27537      });
27538      </code></pre>
27539      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27540      * 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.
27541      * @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}
27542      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27543      * @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.
27544      */
27545     load : function(){
27546         var um = this.el.getUpdateManager();
27547         um.update.apply(um, arguments);
27548     },
27549
27550     // note - render is a standard framework call...
27551     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27552     render : function(el, response){
27553         
27554         this.clearSelections();
27555         this.el.update("");
27556         var o;
27557         try{
27558             if (response != '') {
27559                 o = Roo.util.JSON.decode(response.responseText);
27560                 if(this.jsonRoot){
27561                     
27562                     o = o[this.jsonRoot];
27563                 }
27564             }
27565         } catch(e){
27566         }
27567         /**
27568          * The current JSON data or null
27569          */
27570         this.jsonData = o;
27571         this.beforeRender();
27572         this.refresh();
27573     },
27574
27575 /**
27576  * Get the number of records in the current JSON dataset
27577  * @return {Number}
27578  */
27579     getCount : function(){
27580         return this.jsonData ? this.jsonData.length : 0;
27581     },
27582
27583 /**
27584  * Returns the JSON object for the specified node(s)
27585  * @param {HTMLElement/Array} node The node or an array of nodes
27586  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27587  * you get the JSON object for the node
27588  */
27589     getNodeData : function(node){
27590         if(node instanceof Array){
27591             var data = [];
27592             for(var i = 0, len = node.length; i < len; i++){
27593                 data.push(this.getNodeData(node[i]));
27594             }
27595             return data;
27596         }
27597         return this.jsonData[this.indexOf(node)] || null;
27598     },
27599
27600     beforeRender : function(){
27601         this.snapshot = this.jsonData;
27602         if(this.sortInfo){
27603             this.sort.apply(this, this.sortInfo);
27604         }
27605         this.fireEvent("beforerender", this, this.jsonData);
27606     },
27607
27608     onLoad : function(el, o){
27609         this.fireEvent("load", this, this.jsonData, o);
27610     },
27611
27612     onLoadException : function(el, o){
27613         this.fireEvent("loadexception", this, o);
27614     },
27615
27616 /**
27617  * Filter the data by a specific property.
27618  * @param {String} property A property on your JSON objects
27619  * @param {String/RegExp} value Either string that the property values
27620  * should start with, or a RegExp to test against the property
27621  */
27622     filter : function(property, value){
27623         if(this.jsonData){
27624             var data = [];
27625             var ss = this.snapshot;
27626             if(typeof value == "string"){
27627                 var vlen = value.length;
27628                 if(vlen == 0){
27629                     this.clearFilter();
27630                     return;
27631                 }
27632                 value = value.toLowerCase();
27633                 for(var i = 0, len = ss.length; i < len; i++){
27634                     var o = ss[i];
27635                     if(o[property].substr(0, vlen).toLowerCase() == value){
27636                         data.push(o);
27637                     }
27638                 }
27639             } else if(value.exec){ // regex?
27640                 for(var i = 0, len = ss.length; i < len; i++){
27641                     var o = ss[i];
27642                     if(value.test(o[property])){
27643                         data.push(o);
27644                     }
27645                 }
27646             } else{
27647                 return;
27648             }
27649             this.jsonData = data;
27650             this.refresh();
27651         }
27652     },
27653
27654 /**
27655  * Filter by a function. The passed function will be called with each
27656  * object in the current dataset. If the function returns true the value is kept,
27657  * otherwise it is filtered.
27658  * @param {Function} fn
27659  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27660  */
27661     filterBy : function(fn, scope){
27662         if(this.jsonData){
27663             var data = [];
27664             var ss = this.snapshot;
27665             for(var i = 0, len = ss.length; i < len; i++){
27666                 var o = ss[i];
27667                 if(fn.call(scope || this, o)){
27668                     data.push(o);
27669                 }
27670             }
27671             this.jsonData = data;
27672             this.refresh();
27673         }
27674     },
27675
27676 /**
27677  * Clears the current filter.
27678  */
27679     clearFilter : function(){
27680         if(this.snapshot && this.jsonData != this.snapshot){
27681             this.jsonData = this.snapshot;
27682             this.refresh();
27683         }
27684     },
27685
27686
27687 /**
27688  * Sorts the data for this view and refreshes it.
27689  * @param {String} property A property on your JSON objects to sort on
27690  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27691  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27692  */
27693     sort : function(property, dir, sortType){
27694         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27695         if(this.jsonData){
27696             var p = property;
27697             var dsc = dir && dir.toLowerCase() == "desc";
27698             var f = function(o1, o2){
27699                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27700                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27701                 ;
27702                 if(v1 < v2){
27703                     return dsc ? +1 : -1;
27704                 } else if(v1 > v2){
27705                     return dsc ? -1 : +1;
27706                 } else{
27707                     return 0;
27708                 }
27709             };
27710             this.jsonData.sort(f);
27711             this.refresh();
27712             if(this.jsonData != this.snapshot){
27713                 this.snapshot.sort(f);
27714             }
27715         }
27716     }
27717 });/*
27718  * Based on:
27719  * Ext JS Library 1.1.1
27720  * Copyright(c) 2006-2007, Ext JS, LLC.
27721  *
27722  * Originally Released Under LGPL - original licence link has changed is not relivant.
27723  *
27724  * Fork - LGPL
27725  * <script type="text/javascript">
27726  */
27727  
27728
27729 /**
27730  * @class Roo.ColorPalette
27731  * @extends Roo.Component
27732  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27733  * Here's an example of typical usage:
27734  * <pre><code>
27735 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27736 cp.render('my-div');
27737
27738 cp.on('select', function(palette, selColor){
27739     // do something with selColor
27740 });
27741 </code></pre>
27742  * @constructor
27743  * Create a new ColorPalette
27744  * @param {Object} config The config object
27745  */
27746 Roo.ColorPalette = function(config){
27747     Roo.ColorPalette.superclass.constructor.call(this, config);
27748     this.addEvents({
27749         /**
27750              * @event select
27751              * Fires when a color is selected
27752              * @param {ColorPalette} this
27753              * @param {String} color The 6-digit color hex code (without the # symbol)
27754              */
27755         select: true
27756     });
27757
27758     if(this.handler){
27759         this.on("select", this.handler, this.scope, true);
27760     }
27761 };
27762 Roo.extend(Roo.ColorPalette, Roo.Component, {
27763     /**
27764      * @cfg {String} itemCls
27765      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27766      */
27767     itemCls : "x-color-palette",
27768     /**
27769      * @cfg {String} value
27770      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27771      * the hex codes are case-sensitive.
27772      */
27773     value : null,
27774     clickEvent:'click',
27775     // private
27776     ctype: "Roo.ColorPalette",
27777
27778     /**
27779      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27780      */
27781     allowReselect : false,
27782
27783     /**
27784      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27785      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27786      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27787      * of colors with the width setting until the box is symmetrical.</p>
27788      * <p>You can override individual colors if needed:</p>
27789      * <pre><code>
27790 var cp = new Roo.ColorPalette();
27791 cp.colors[0] = "FF0000";  // change the first box to red
27792 </code></pre>
27793
27794 Or you can provide a custom array of your own for complete control:
27795 <pre><code>
27796 var cp = new Roo.ColorPalette();
27797 cp.colors = ["000000", "993300", "333300"];
27798 </code></pre>
27799      * @type Array
27800      */
27801     colors : [
27802         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27803         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27804         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27805         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27806         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27807     ],
27808
27809     // private
27810     onRender : function(container, position){
27811         var t = new Roo.MasterTemplate(
27812             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27813         );
27814         var c = this.colors;
27815         for(var i = 0, len = c.length; i < len; i++){
27816             t.add([c[i]]);
27817         }
27818         var el = document.createElement("div");
27819         el.className = this.itemCls;
27820         t.overwrite(el);
27821         container.dom.insertBefore(el, position);
27822         this.el = Roo.get(el);
27823         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27824         if(this.clickEvent != 'click'){
27825             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27826         }
27827     },
27828
27829     // private
27830     afterRender : function(){
27831         Roo.ColorPalette.superclass.afterRender.call(this);
27832         if(this.value){
27833             var s = this.value;
27834             this.value = null;
27835             this.select(s);
27836         }
27837     },
27838
27839     // private
27840     handleClick : function(e, t){
27841         e.preventDefault();
27842         if(!this.disabled){
27843             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27844             this.select(c.toUpperCase());
27845         }
27846     },
27847
27848     /**
27849      * Selects the specified color in the palette (fires the select event)
27850      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27851      */
27852     select : function(color){
27853         color = color.replace("#", "");
27854         if(color != this.value || this.allowReselect){
27855             var el = this.el;
27856             if(this.value){
27857                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27858             }
27859             el.child("a.color-"+color).addClass("x-color-palette-sel");
27860             this.value = color;
27861             this.fireEvent("select", this, color);
27862         }
27863     }
27864 });/*
27865  * Based on:
27866  * Ext JS Library 1.1.1
27867  * Copyright(c) 2006-2007, Ext JS, LLC.
27868  *
27869  * Originally Released Under LGPL - original licence link has changed is not relivant.
27870  *
27871  * Fork - LGPL
27872  * <script type="text/javascript">
27873  */
27874  
27875 /**
27876  * @class Roo.DatePicker
27877  * @extends Roo.Component
27878  * Simple date picker class.
27879  * @constructor
27880  * Create a new DatePicker
27881  * @param {Object} config The config object
27882  */
27883 Roo.DatePicker = function(config){
27884     Roo.DatePicker.superclass.constructor.call(this, config);
27885
27886     this.value = config && config.value ?
27887                  config.value.clearTime() : new Date().clearTime();
27888
27889     this.addEvents({
27890         /**
27891              * @event select
27892              * Fires when a date is selected
27893              * @param {DatePicker} this
27894              * @param {Date} date The selected date
27895              */
27896         'select': true,
27897         /**
27898              * @event monthchange
27899              * Fires when the displayed month changes 
27900              * @param {DatePicker} this
27901              * @param {Date} date The selected month
27902              */
27903         'monthchange': true
27904     });
27905
27906     if(this.handler){
27907         this.on("select", this.handler,  this.scope || this);
27908     }
27909     // build the disabledDatesRE
27910     if(!this.disabledDatesRE && this.disabledDates){
27911         var dd = this.disabledDates;
27912         var re = "(?:";
27913         for(var i = 0; i < dd.length; i++){
27914             re += dd[i];
27915             if(i != dd.length-1) {
27916                 re += "|";
27917             }
27918         }
27919         this.disabledDatesRE = new RegExp(re + ")");
27920     }
27921 };
27922
27923 Roo.extend(Roo.DatePicker, Roo.Component, {
27924     /**
27925      * @cfg {String} todayText
27926      * The text to display on the button that selects the current date (defaults to "Today")
27927      */
27928     todayText : "Today",
27929     /**
27930      * @cfg {String} okText
27931      * The text to display on the ok button
27932      */
27933     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27934     /**
27935      * @cfg {String} cancelText
27936      * The text to display on the cancel button
27937      */
27938     cancelText : "Cancel",
27939     /**
27940      * @cfg {String} todayTip
27941      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27942      */
27943     todayTip : "{0} (Spacebar)",
27944     /**
27945      * @cfg {Date} minDate
27946      * Minimum allowable date (JavaScript date object, defaults to null)
27947      */
27948     minDate : null,
27949     /**
27950      * @cfg {Date} maxDate
27951      * Maximum allowable date (JavaScript date object, defaults to null)
27952      */
27953     maxDate : null,
27954     /**
27955      * @cfg {String} minText
27956      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27957      */
27958     minText : "This date is before the minimum date",
27959     /**
27960      * @cfg {String} maxText
27961      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27962      */
27963     maxText : "This date is after the maximum date",
27964     /**
27965      * @cfg {String} format
27966      * The default date format string which can be overriden for localization support.  The format must be
27967      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27968      */
27969     format : "m/d/y",
27970     /**
27971      * @cfg {Array} disabledDays
27972      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27973      */
27974     disabledDays : null,
27975     /**
27976      * @cfg {String} disabledDaysText
27977      * The tooltip to display when the date falls on a disabled day (defaults to "")
27978      */
27979     disabledDaysText : "",
27980     /**
27981      * @cfg {RegExp} disabledDatesRE
27982      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27983      */
27984     disabledDatesRE : null,
27985     /**
27986      * @cfg {String} disabledDatesText
27987      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27988      */
27989     disabledDatesText : "",
27990     /**
27991      * @cfg {Boolean} constrainToViewport
27992      * True to constrain the date picker to the viewport (defaults to true)
27993      */
27994     constrainToViewport : true,
27995     /**
27996      * @cfg {Array} monthNames
27997      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27998      */
27999     monthNames : Date.monthNames,
28000     /**
28001      * @cfg {Array} dayNames
28002      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28003      */
28004     dayNames : Date.dayNames,
28005     /**
28006      * @cfg {String} nextText
28007      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28008      */
28009     nextText: 'Next Month (Control+Right)',
28010     /**
28011      * @cfg {String} prevText
28012      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28013      */
28014     prevText: 'Previous Month (Control+Left)',
28015     /**
28016      * @cfg {String} monthYearText
28017      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28018      */
28019     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28020     /**
28021      * @cfg {Number} startDay
28022      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28023      */
28024     startDay : 0,
28025     /**
28026      * @cfg {Bool} showClear
28027      * Show a clear button (usefull for date form elements that can be blank.)
28028      */
28029     
28030     showClear: false,
28031     
28032     /**
28033      * Sets the value of the date field
28034      * @param {Date} value The date to set
28035      */
28036     setValue : function(value){
28037         var old = this.value;
28038         
28039         if (typeof(value) == 'string') {
28040          
28041             value = Date.parseDate(value, this.format);
28042         }
28043         if (!value) {
28044             value = new Date();
28045         }
28046         
28047         this.value = value.clearTime(true);
28048         if(this.el){
28049             this.update(this.value);
28050         }
28051     },
28052
28053     /**
28054      * Gets the current selected value of the date field
28055      * @return {Date} The selected date
28056      */
28057     getValue : function(){
28058         return this.value;
28059     },
28060
28061     // private
28062     focus : function(){
28063         if(this.el){
28064             this.update(this.activeDate);
28065         }
28066     },
28067
28068     // privateval
28069     onRender : function(container, position){
28070         
28071         var m = [
28072              '<table cellspacing="0">',
28073                 '<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>',
28074                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28075         var dn = this.dayNames;
28076         for(var i = 0; i < 7; i++){
28077             var d = this.startDay+i;
28078             if(d > 6){
28079                 d = d-7;
28080             }
28081             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28082         }
28083         m[m.length] = "</tr></thead><tbody><tr>";
28084         for(var i = 0; i < 42; i++) {
28085             if(i % 7 == 0 && i != 0){
28086                 m[m.length] = "</tr><tr>";
28087             }
28088             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28089         }
28090         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28091             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28092
28093         var el = document.createElement("div");
28094         el.className = "x-date-picker";
28095         el.innerHTML = m.join("");
28096
28097         container.dom.insertBefore(el, position);
28098
28099         this.el = Roo.get(el);
28100         this.eventEl = Roo.get(el.firstChild);
28101
28102         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28103             handler: this.showPrevMonth,
28104             scope: this,
28105             preventDefault:true,
28106             stopDefault:true
28107         });
28108
28109         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
28110             handler: this.showNextMonth,
28111             scope: this,
28112             preventDefault:true,
28113             stopDefault:true
28114         });
28115
28116         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
28117
28118         this.monthPicker = this.el.down('div.x-date-mp');
28119         this.monthPicker.enableDisplayMode('block');
28120         
28121         var kn = new Roo.KeyNav(this.eventEl, {
28122             "left" : function(e){
28123                 e.ctrlKey ?
28124                     this.showPrevMonth() :
28125                     this.update(this.activeDate.add("d", -1));
28126             },
28127
28128             "right" : function(e){
28129                 e.ctrlKey ?
28130                     this.showNextMonth() :
28131                     this.update(this.activeDate.add("d", 1));
28132             },
28133
28134             "up" : function(e){
28135                 e.ctrlKey ?
28136                     this.showNextYear() :
28137                     this.update(this.activeDate.add("d", -7));
28138             },
28139
28140             "down" : function(e){
28141                 e.ctrlKey ?
28142                     this.showPrevYear() :
28143                     this.update(this.activeDate.add("d", 7));
28144             },
28145
28146             "pageUp" : function(e){
28147                 this.showNextMonth();
28148             },
28149
28150             "pageDown" : function(e){
28151                 this.showPrevMonth();
28152             },
28153
28154             "enter" : function(e){
28155                 e.stopPropagation();
28156                 return true;
28157             },
28158
28159             scope : this
28160         });
28161
28162         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28163
28164         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28165
28166         this.el.unselectable();
28167         
28168         this.cells = this.el.select("table.x-date-inner tbody td");
28169         this.textNodes = this.el.query("table.x-date-inner tbody span");
28170
28171         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28172             text: "&#160;",
28173             tooltip: this.monthYearText
28174         });
28175
28176         this.mbtn.on('click', this.showMonthPicker, this);
28177         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28178
28179
28180         var today = (new Date()).dateFormat(this.format);
28181         
28182         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28183         if (this.showClear) {
28184             baseTb.add( new Roo.Toolbar.Fill());
28185         }
28186         baseTb.add({
28187             text: String.format(this.todayText, today),
28188             tooltip: String.format(this.todayTip, today),
28189             handler: this.selectToday,
28190             scope: this
28191         });
28192         
28193         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28194             
28195         //});
28196         if (this.showClear) {
28197             
28198             baseTb.add( new Roo.Toolbar.Fill());
28199             baseTb.add({
28200                 text: '&#160;',
28201                 cls: 'x-btn-icon x-btn-clear',
28202                 handler: function() {
28203                     //this.value = '';
28204                     this.fireEvent("select", this, '');
28205                 },
28206                 scope: this
28207             });
28208         }
28209         
28210         
28211         if(Roo.isIE){
28212             this.el.repaint();
28213         }
28214         this.update(this.value);
28215     },
28216
28217     createMonthPicker : function(){
28218         if(!this.monthPicker.dom.firstChild){
28219             var buf = ['<table border="0" cellspacing="0">'];
28220             for(var i = 0; i < 6; i++){
28221                 buf.push(
28222                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28223                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28224                     i == 0 ?
28225                     '<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>' :
28226                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28227                 );
28228             }
28229             buf.push(
28230                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28231                     this.okText,
28232                     '</button><button type="button" class="x-date-mp-cancel">',
28233                     this.cancelText,
28234                     '</button></td></tr>',
28235                 '</table>'
28236             );
28237             this.monthPicker.update(buf.join(''));
28238             this.monthPicker.on('click', this.onMonthClick, this);
28239             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28240
28241             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28242             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28243
28244             this.mpMonths.each(function(m, a, i){
28245                 i += 1;
28246                 if((i%2) == 0){
28247                     m.dom.xmonth = 5 + Math.round(i * .5);
28248                 }else{
28249                     m.dom.xmonth = Math.round((i-1) * .5);
28250                 }
28251             });
28252         }
28253     },
28254
28255     showMonthPicker : function(){
28256         this.createMonthPicker();
28257         var size = this.el.getSize();
28258         this.monthPicker.setSize(size);
28259         this.monthPicker.child('table').setSize(size);
28260
28261         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28262         this.updateMPMonth(this.mpSelMonth);
28263         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28264         this.updateMPYear(this.mpSelYear);
28265
28266         this.monthPicker.slideIn('t', {duration:.2});
28267     },
28268
28269     updateMPYear : function(y){
28270         this.mpyear = y;
28271         var ys = this.mpYears.elements;
28272         for(var i = 1; i <= 10; i++){
28273             var td = ys[i-1], y2;
28274             if((i%2) == 0){
28275                 y2 = y + Math.round(i * .5);
28276                 td.firstChild.innerHTML = y2;
28277                 td.xyear = y2;
28278             }else{
28279                 y2 = y - (5-Math.round(i * .5));
28280                 td.firstChild.innerHTML = y2;
28281                 td.xyear = y2;
28282             }
28283             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28284         }
28285     },
28286
28287     updateMPMonth : function(sm){
28288         this.mpMonths.each(function(m, a, i){
28289             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28290         });
28291     },
28292
28293     selectMPMonth: function(m){
28294         
28295     },
28296
28297     onMonthClick : function(e, t){
28298         e.stopEvent();
28299         var el = new Roo.Element(t), pn;
28300         if(el.is('button.x-date-mp-cancel')){
28301             this.hideMonthPicker();
28302         }
28303         else if(el.is('button.x-date-mp-ok')){
28304             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28305             this.hideMonthPicker();
28306         }
28307         else if(pn = el.up('td.x-date-mp-month', 2)){
28308             this.mpMonths.removeClass('x-date-mp-sel');
28309             pn.addClass('x-date-mp-sel');
28310             this.mpSelMonth = pn.dom.xmonth;
28311         }
28312         else if(pn = el.up('td.x-date-mp-year', 2)){
28313             this.mpYears.removeClass('x-date-mp-sel');
28314             pn.addClass('x-date-mp-sel');
28315             this.mpSelYear = pn.dom.xyear;
28316         }
28317         else if(el.is('a.x-date-mp-prev')){
28318             this.updateMPYear(this.mpyear-10);
28319         }
28320         else if(el.is('a.x-date-mp-next')){
28321             this.updateMPYear(this.mpyear+10);
28322         }
28323     },
28324
28325     onMonthDblClick : function(e, t){
28326         e.stopEvent();
28327         var el = new Roo.Element(t), pn;
28328         if(pn = el.up('td.x-date-mp-month', 2)){
28329             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28330             this.hideMonthPicker();
28331         }
28332         else if(pn = el.up('td.x-date-mp-year', 2)){
28333             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28334             this.hideMonthPicker();
28335         }
28336     },
28337
28338     hideMonthPicker : function(disableAnim){
28339         if(this.monthPicker){
28340             if(disableAnim === true){
28341                 this.monthPicker.hide();
28342             }else{
28343                 this.monthPicker.slideOut('t', {duration:.2});
28344             }
28345         }
28346     },
28347
28348     // private
28349     showPrevMonth : function(e){
28350         this.update(this.activeDate.add("mo", -1));
28351     },
28352
28353     // private
28354     showNextMonth : function(e){
28355         this.update(this.activeDate.add("mo", 1));
28356     },
28357
28358     // private
28359     showPrevYear : function(){
28360         this.update(this.activeDate.add("y", -1));
28361     },
28362
28363     // private
28364     showNextYear : function(){
28365         this.update(this.activeDate.add("y", 1));
28366     },
28367
28368     // private
28369     handleMouseWheel : function(e){
28370         var delta = e.getWheelDelta();
28371         if(delta > 0){
28372             this.showPrevMonth();
28373             e.stopEvent();
28374         } else if(delta < 0){
28375             this.showNextMonth();
28376             e.stopEvent();
28377         }
28378     },
28379
28380     // private
28381     handleDateClick : function(e, t){
28382         e.stopEvent();
28383         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28384             this.setValue(new Date(t.dateValue));
28385             this.fireEvent("select", this, this.value);
28386         }
28387     },
28388
28389     // private
28390     selectToday : function(){
28391         this.setValue(new Date().clearTime());
28392         this.fireEvent("select", this, this.value);
28393     },
28394
28395     // private
28396     update : function(date)
28397     {
28398         var vd = this.activeDate;
28399         this.activeDate = date;
28400         if(vd && this.el){
28401             var t = date.getTime();
28402             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28403                 this.cells.removeClass("x-date-selected");
28404                 this.cells.each(function(c){
28405                    if(c.dom.firstChild.dateValue == t){
28406                        c.addClass("x-date-selected");
28407                        setTimeout(function(){
28408                             try{c.dom.firstChild.focus();}catch(e){}
28409                        }, 50);
28410                        return false;
28411                    }
28412                 });
28413                 return;
28414             }
28415         }
28416         
28417         var days = date.getDaysInMonth();
28418         var firstOfMonth = date.getFirstDateOfMonth();
28419         var startingPos = firstOfMonth.getDay()-this.startDay;
28420
28421         if(startingPos <= this.startDay){
28422             startingPos += 7;
28423         }
28424
28425         var pm = date.add("mo", -1);
28426         var prevStart = pm.getDaysInMonth()-startingPos;
28427
28428         var cells = this.cells.elements;
28429         var textEls = this.textNodes;
28430         days += startingPos;
28431
28432         // convert everything to numbers so it's fast
28433         var day = 86400000;
28434         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28435         var today = new Date().clearTime().getTime();
28436         var sel = date.clearTime().getTime();
28437         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28438         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28439         var ddMatch = this.disabledDatesRE;
28440         var ddText = this.disabledDatesText;
28441         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28442         var ddaysText = this.disabledDaysText;
28443         var format = this.format;
28444
28445         var setCellClass = function(cal, cell){
28446             cell.title = "";
28447             var t = d.getTime();
28448             cell.firstChild.dateValue = t;
28449             if(t == today){
28450                 cell.className += " x-date-today";
28451                 cell.title = cal.todayText;
28452             }
28453             if(t == sel){
28454                 cell.className += " x-date-selected";
28455                 setTimeout(function(){
28456                     try{cell.firstChild.focus();}catch(e){}
28457                 }, 50);
28458             }
28459             // disabling
28460             if(t < min) {
28461                 cell.className = " x-date-disabled";
28462                 cell.title = cal.minText;
28463                 return;
28464             }
28465             if(t > max) {
28466                 cell.className = " x-date-disabled";
28467                 cell.title = cal.maxText;
28468                 return;
28469             }
28470             if(ddays){
28471                 if(ddays.indexOf(d.getDay()) != -1){
28472                     cell.title = ddaysText;
28473                     cell.className = " x-date-disabled";
28474                 }
28475             }
28476             if(ddMatch && format){
28477                 var fvalue = d.dateFormat(format);
28478                 if(ddMatch.test(fvalue)){
28479                     cell.title = ddText.replace("%0", fvalue);
28480                     cell.className = " x-date-disabled";
28481                 }
28482             }
28483         };
28484
28485         var i = 0;
28486         for(; i < startingPos; i++) {
28487             textEls[i].innerHTML = (++prevStart);
28488             d.setDate(d.getDate()+1);
28489             cells[i].className = "x-date-prevday";
28490             setCellClass(this, cells[i]);
28491         }
28492         for(; i < days; i++){
28493             intDay = i - startingPos + 1;
28494             textEls[i].innerHTML = (intDay);
28495             d.setDate(d.getDate()+1);
28496             cells[i].className = "x-date-active";
28497             setCellClass(this, cells[i]);
28498         }
28499         var extraDays = 0;
28500         for(; i < 42; i++) {
28501              textEls[i].innerHTML = (++extraDays);
28502              d.setDate(d.getDate()+1);
28503              cells[i].className = "x-date-nextday";
28504              setCellClass(this, cells[i]);
28505         }
28506
28507         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28508         this.fireEvent('monthchange', this, date);
28509         
28510         if(!this.internalRender){
28511             var main = this.el.dom.firstChild;
28512             var w = main.offsetWidth;
28513             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28514             Roo.fly(main).setWidth(w);
28515             this.internalRender = true;
28516             // opera does not respect the auto grow header center column
28517             // then, after it gets a width opera refuses to recalculate
28518             // without a second pass
28519             if(Roo.isOpera && !this.secondPass){
28520                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28521                 this.secondPass = true;
28522                 this.update.defer(10, this, [date]);
28523             }
28524         }
28525         
28526         
28527     }
28528 });        /*
28529  * Based on:
28530  * Ext JS Library 1.1.1
28531  * Copyright(c) 2006-2007, Ext JS, LLC.
28532  *
28533  * Originally Released Under LGPL - original licence link has changed is not relivant.
28534  *
28535  * Fork - LGPL
28536  * <script type="text/javascript">
28537  */
28538 /**
28539  * @class Roo.TabPanel
28540  * @extends Roo.util.Observable
28541  * A lightweight tab container.
28542  * <br><br>
28543  * Usage:
28544  * <pre><code>
28545 // basic tabs 1, built from existing content
28546 var tabs = new Roo.TabPanel("tabs1");
28547 tabs.addTab("script", "View Script");
28548 tabs.addTab("markup", "View Markup");
28549 tabs.activate("script");
28550
28551 // more advanced tabs, built from javascript
28552 var jtabs = new Roo.TabPanel("jtabs");
28553 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28554
28555 // set up the UpdateManager
28556 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28557 var updater = tab2.getUpdateManager();
28558 updater.setDefaultUrl("ajax1.htm");
28559 tab2.on('activate', updater.refresh, updater, true);
28560
28561 // Use setUrl for Ajax loading
28562 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28563 tab3.setUrl("ajax2.htm", null, true);
28564
28565 // Disabled tab
28566 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28567 tab4.disable();
28568
28569 jtabs.activate("jtabs-1");
28570  * </code></pre>
28571  * @constructor
28572  * Create a new TabPanel.
28573  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28574  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28575  */
28576 Roo.TabPanel = function(container, config){
28577     /**
28578     * The container element for this TabPanel.
28579     * @type Roo.Element
28580     */
28581     this.el = Roo.get(container, true);
28582     if(config){
28583         if(typeof config == "boolean"){
28584             this.tabPosition = config ? "bottom" : "top";
28585         }else{
28586             Roo.apply(this, config);
28587         }
28588     }
28589     if(this.tabPosition == "bottom"){
28590         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28591         this.el.addClass("x-tabs-bottom");
28592     }
28593     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28594     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28595     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28596     if(Roo.isIE){
28597         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28598     }
28599     if(this.tabPosition != "bottom"){
28600         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28601          * @type Roo.Element
28602          */
28603         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28604         this.el.addClass("x-tabs-top");
28605     }
28606     this.items = [];
28607
28608     this.bodyEl.setStyle("position", "relative");
28609
28610     this.active = null;
28611     this.activateDelegate = this.activate.createDelegate(this);
28612
28613     this.addEvents({
28614         /**
28615          * @event tabchange
28616          * Fires when the active tab changes
28617          * @param {Roo.TabPanel} this
28618          * @param {Roo.TabPanelItem} activePanel The new active tab
28619          */
28620         "tabchange": true,
28621         /**
28622          * @event beforetabchange
28623          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28624          * @param {Roo.TabPanel} this
28625          * @param {Object} e Set cancel to true on this object to cancel the tab change
28626          * @param {Roo.TabPanelItem} tab The tab being changed to
28627          */
28628         "beforetabchange" : true
28629     });
28630
28631     Roo.EventManager.onWindowResize(this.onResize, this);
28632     this.cpad = this.el.getPadding("lr");
28633     this.hiddenCount = 0;
28634
28635
28636     // toolbar on the tabbar support...
28637     if (this.toolbar) {
28638         var tcfg = this.toolbar;
28639         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28640         this.toolbar = new Roo.Toolbar(tcfg);
28641         if (Roo.isSafari) {
28642             var tbl = tcfg.container.child('table', true);
28643             tbl.setAttribute('width', '100%');
28644         }
28645         
28646     }
28647    
28648
28649
28650     Roo.TabPanel.superclass.constructor.call(this);
28651 };
28652
28653 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28654     /*
28655      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28656      */
28657     tabPosition : "top",
28658     /*
28659      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28660      */
28661     currentTabWidth : 0,
28662     /*
28663      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28664      */
28665     minTabWidth : 40,
28666     /*
28667      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28668      */
28669     maxTabWidth : 250,
28670     /*
28671      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28672      */
28673     preferredTabWidth : 175,
28674     /*
28675      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28676      */
28677     resizeTabs : false,
28678     /*
28679      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28680      */
28681     monitorResize : true,
28682     /*
28683      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28684      */
28685     toolbar : false,
28686
28687     /**
28688      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28689      * @param {String} id The id of the div to use <b>or create</b>
28690      * @param {String} text The text for the tab
28691      * @param {String} content (optional) Content to put in the TabPanelItem body
28692      * @param {Boolean} closable (optional) True to create a close icon on the tab
28693      * @return {Roo.TabPanelItem} The created TabPanelItem
28694      */
28695     addTab : function(id, text, content, closable){
28696         var item = new Roo.TabPanelItem(this, id, text, closable);
28697         this.addTabItem(item);
28698         if(content){
28699             item.setContent(content);
28700         }
28701         return item;
28702     },
28703
28704     /**
28705      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28706      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28707      * @return {Roo.TabPanelItem}
28708      */
28709     getTab : function(id){
28710         return this.items[id];
28711     },
28712
28713     /**
28714      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28715      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28716      */
28717     hideTab : function(id){
28718         var t = this.items[id];
28719         if(!t.isHidden()){
28720            t.setHidden(true);
28721            this.hiddenCount++;
28722            this.autoSizeTabs();
28723         }
28724     },
28725
28726     /**
28727      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28728      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28729      */
28730     unhideTab : function(id){
28731         var t = this.items[id];
28732         if(t.isHidden()){
28733            t.setHidden(false);
28734            this.hiddenCount--;
28735            this.autoSizeTabs();
28736         }
28737     },
28738
28739     /**
28740      * Adds an existing {@link Roo.TabPanelItem}.
28741      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28742      */
28743     addTabItem : function(item){
28744         this.items[item.id] = item;
28745         this.items.push(item);
28746         if(this.resizeTabs){
28747            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28748            this.autoSizeTabs();
28749         }else{
28750             item.autoSize();
28751         }
28752     },
28753
28754     /**
28755      * Removes a {@link Roo.TabPanelItem}.
28756      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28757      */
28758     removeTab : function(id){
28759         var items = this.items;
28760         var tab = items[id];
28761         if(!tab) { return; }
28762         var index = items.indexOf(tab);
28763         if(this.active == tab && items.length > 1){
28764             var newTab = this.getNextAvailable(index);
28765             if(newTab) {
28766                 newTab.activate();
28767             }
28768         }
28769         this.stripEl.dom.removeChild(tab.pnode.dom);
28770         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28771             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28772         }
28773         items.splice(index, 1);
28774         delete this.items[tab.id];
28775         tab.fireEvent("close", tab);
28776         tab.purgeListeners();
28777         this.autoSizeTabs();
28778     },
28779
28780     getNextAvailable : function(start){
28781         var items = this.items;
28782         var index = start;
28783         // look for a next tab that will slide over to
28784         // replace the one being removed
28785         while(index < items.length){
28786             var item = items[++index];
28787             if(item && !item.isHidden()){
28788                 return item;
28789             }
28790         }
28791         // if one isn't found select the previous tab (on the left)
28792         index = start;
28793         while(index >= 0){
28794             var item = items[--index];
28795             if(item && !item.isHidden()){
28796                 return item;
28797             }
28798         }
28799         return null;
28800     },
28801
28802     /**
28803      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28804      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28805      */
28806     disableTab : function(id){
28807         var tab = this.items[id];
28808         if(tab && this.active != tab){
28809             tab.disable();
28810         }
28811     },
28812
28813     /**
28814      * Enables a {@link Roo.TabPanelItem} that is disabled.
28815      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28816      */
28817     enableTab : function(id){
28818         var tab = this.items[id];
28819         tab.enable();
28820     },
28821
28822     /**
28823      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28824      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28825      * @return {Roo.TabPanelItem} The TabPanelItem.
28826      */
28827     activate : function(id){
28828         var tab = this.items[id];
28829         if(!tab){
28830             return null;
28831         }
28832         if(tab == this.active || tab.disabled){
28833             return tab;
28834         }
28835         var e = {};
28836         this.fireEvent("beforetabchange", this, e, tab);
28837         if(e.cancel !== true && !tab.disabled){
28838             if(this.active){
28839                 this.active.hide();
28840             }
28841             this.active = this.items[id];
28842             this.active.show();
28843             this.fireEvent("tabchange", this, this.active);
28844         }
28845         return tab;
28846     },
28847
28848     /**
28849      * Gets the active {@link Roo.TabPanelItem}.
28850      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28851      */
28852     getActiveTab : function(){
28853         return this.active;
28854     },
28855
28856     /**
28857      * Updates the tab body element to fit the height of the container element
28858      * for overflow scrolling
28859      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28860      */
28861     syncHeight : function(targetHeight){
28862         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28863         var bm = this.bodyEl.getMargins();
28864         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28865         this.bodyEl.setHeight(newHeight);
28866         return newHeight;
28867     },
28868
28869     onResize : function(){
28870         if(this.monitorResize){
28871             this.autoSizeTabs();
28872         }
28873     },
28874
28875     /**
28876      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28877      */
28878     beginUpdate : function(){
28879         this.updating = true;
28880     },
28881
28882     /**
28883      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28884      */
28885     endUpdate : function(){
28886         this.updating = false;
28887         this.autoSizeTabs();
28888     },
28889
28890     /**
28891      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28892      */
28893     autoSizeTabs : function(){
28894         var count = this.items.length;
28895         var vcount = count - this.hiddenCount;
28896         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28897             return;
28898         }
28899         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28900         var availWidth = Math.floor(w / vcount);
28901         var b = this.stripBody;
28902         if(b.getWidth() > w){
28903             var tabs = this.items;
28904             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28905             if(availWidth < this.minTabWidth){
28906                 /*if(!this.sleft){    // incomplete scrolling code
28907                     this.createScrollButtons();
28908                 }
28909                 this.showScroll();
28910                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28911             }
28912         }else{
28913             if(this.currentTabWidth < this.preferredTabWidth){
28914                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28915             }
28916         }
28917     },
28918
28919     /**
28920      * Returns the number of tabs in this TabPanel.
28921      * @return {Number}
28922      */
28923      getCount : function(){
28924          return this.items.length;
28925      },
28926
28927     /**
28928      * Resizes all the tabs to the passed width
28929      * @param {Number} The new width
28930      */
28931     setTabWidth : function(width){
28932         this.currentTabWidth = width;
28933         for(var i = 0, len = this.items.length; i < len; i++) {
28934                 if(!this.items[i].isHidden()) {
28935                 this.items[i].setWidth(width);
28936             }
28937         }
28938     },
28939
28940     /**
28941      * Destroys this TabPanel
28942      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28943      */
28944     destroy : function(removeEl){
28945         Roo.EventManager.removeResizeListener(this.onResize, this);
28946         for(var i = 0, len = this.items.length; i < len; i++){
28947             this.items[i].purgeListeners();
28948         }
28949         if(removeEl === true){
28950             this.el.update("");
28951             this.el.remove();
28952         }
28953     }
28954 });
28955
28956 /**
28957  * @class Roo.TabPanelItem
28958  * @extends Roo.util.Observable
28959  * Represents an individual item (tab plus body) in a TabPanel.
28960  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28961  * @param {String} id The id of this TabPanelItem
28962  * @param {String} text The text for the tab of this TabPanelItem
28963  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28964  */
28965 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28966     /**
28967      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28968      * @type Roo.TabPanel
28969      */
28970     this.tabPanel = tabPanel;
28971     /**
28972      * The id for this TabPanelItem
28973      * @type String
28974      */
28975     this.id = id;
28976     /** @private */
28977     this.disabled = false;
28978     /** @private */
28979     this.text = text;
28980     /** @private */
28981     this.loaded = false;
28982     this.closable = closable;
28983
28984     /**
28985      * The body element for this TabPanelItem.
28986      * @type Roo.Element
28987      */
28988     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28989     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28990     this.bodyEl.setStyle("display", "block");
28991     this.bodyEl.setStyle("zoom", "1");
28992     this.hideAction();
28993
28994     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28995     /** @private */
28996     this.el = Roo.get(els.el, true);
28997     this.inner = Roo.get(els.inner, true);
28998     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28999     this.pnode = Roo.get(els.el.parentNode, true);
29000     this.el.on("mousedown", this.onTabMouseDown, this);
29001     this.el.on("click", this.onTabClick, this);
29002     /** @private */
29003     if(closable){
29004         var c = Roo.get(els.close, true);
29005         c.dom.title = this.closeText;
29006         c.addClassOnOver("close-over");
29007         c.on("click", this.closeClick, this);
29008      }
29009
29010     this.addEvents({
29011          /**
29012          * @event activate
29013          * Fires when this tab becomes the active tab.
29014          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29015          * @param {Roo.TabPanelItem} this
29016          */
29017         "activate": true,
29018         /**
29019          * @event beforeclose
29020          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29021          * @param {Roo.TabPanelItem} this
29022          * @param {Object} e Set cancel to true on this object to cancel the close.
29023          */
29024         "beforeclose": true,
29025         /**
29026          * @event close
29027          * Fires when this tab is closed.
29028          * @param {Roo.TabPanelItem} this
29029          */
29030          "close": true,
29031         /**
29032          * @event deactivate
29033          * Fires when this tab is no longer the active tab.
29034          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29035          * @param {Roo.TabPanelItem} this
29036          */
29037          "deactivate" : true
29038     });
29039     this.hidden = false;
29040
29041     Roo.TabPanelItem.superclass.constructor.call(this);
29042 };
29043
29044 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29045     purgeListeners : function(){
29046        Roo.util.Observable.prototype.purgeListeners.call(this);
29047        this.el.removeAllListeners();
29048     },
29049     /**
29050      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29051      */
29052     show : function(){
29053         this.pnode.addClass("on");
29054         this.showAction();
29055         if(Roo.isOpera){
29056             this.tabPanel.stripWrap.repaint();
29057         }
29058         this.fireEvent("activate", this.tabPanel, this);
29059     },
29060
29061     /**
29062      * Returns true if this tab is the active tab.
29063      * @return {Boolean}
29064      */
29065     isActive : function(){
29066         return this.tabPanel.getActiveTab() == this;
29067     },
29068
29069     /**
29070      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29071      */
29072     hide : function(){
29073         this.pnode.removeClass("on");
29074         this.hideAction();
29075         this.fireEvent("deactivate", this.tabPanel, this);
29076     },
29077
29078     hideAction : function(){
29079         this.bodyEl.hide();
29080         this.bodyEl.setStyle("position", "absolute");
29081         this.bodyEl.setLeft("-20000px");
29082         this.bodyEl.setTop("-20000px");
29083     },
29084
29085     showAction : function(){
29086         this.bodyEl.setStyle("position", "relative");
29087         this.bodyEl.setTop("");
29088         this.bodyEl.setLeft("");
29089         this.bodyEl.show();
29090     },
29091
29092     /**
29093      * Set the tooltip for the tab.
29094      * @param {String} tooltip The tab's tooltip
29095      */
29096     setTooltip : function(text){
29097         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29098             this.textEl.dom.qtip = text;
29099             this.textEl.dom.removeAttribute('title');
29100         }else{
29101             this.textEl.dom.title = text;
29102         }
29103     },
29104
29105     onTabClick : function(e){
29106         e.preventDefault();
29107         this.tabPanel.activate(this.id);
29108     },
29109
29110     onTabMouseDown : function(e){
29111         e.preventDefault();
29112         this.tabPanel.activate(this.id);
29113     },
29114
29115     getWidth : function(){
29116         return this.inner.getWidth();
29117     },
29118
29119     setWidth : function(width){
29120         var iwidth = width - this.pnode.getPadding("lr");
29121         this.inner.setWidth(iwidth);
29122         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
29123         this.pnode.setWidth(width);
29124     },
29125
29126     /**
29127      * Show or hide the tab
29128      * @param {Boolean} hidden True to hide or false to show.
29129      */
29130     setHidden : function(hidden){
29131         this.hidden = hidden;
29132         this.pnode.setStyle("display", hidden ? "none" : "");
29133     },
29134
29135     /**
29136      * Returns true if this tab is "hidden"
29137      * @return {Boolean}
29138      */
29139     isHidden : function(){
29140         return this.hidden;
29141     },
29142
29143     /**
29144      * Returns the text for this tab
29145      * @return {String}
29146      */
29147     getText : function(){
29148         return this.text;
29149     },
29150
29151     autoSize : function(){
29152         //this.el.beginMeasure();
29153         this.textEl.setWidth(1);
29154         /*
29155          *  #2804 [new] Tabs in Roojs
29156          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29157          */
29158         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29159         //this.el.endMeasure();
29160     },
29161
29162     /**
29163      * Sets the text for the tab (Note: this also sets the tooltip text)
29164      * @param {String} text The tab's text and tooltip
29165      */
29166     setText : function(text){
29167         this.text = text;
29168         this.textEl.update(text);
29169         this.setTooltip(text);
29170         if(!this.tabPanel.resizeTabs){
29171             this.autoSize();
29172         }
29173     },
29174     /**
29175      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29176      */
29177     activate : function(){
29178         this.tabPanel.activate(this.id);
29179     },
29180
29181     /**
29182      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29183      */
29184     disable : function(){
29185         if(this.tabPanel.active != this){
29186             this.disabled = true;
29187             this.pnode.addClass("disabled");
29188         }
29189     },
29190
29191     /**
29192      * Enables this TabPanelItem if it was previously disabled.
29193      */
29194     enable : function(){
29195         this.disabled = false;
29196         this.pnode.removeClass("disabled");
29197     },
29198
29199     /**
29200      * Sets the content for this TabPanelItem.
29201      * @param {String} content The content
29202      * @param {Boolean} loadScripts true to look for and load scripts
29203      */
29204     setContent : function(content, loadScripts){
29205         this.bodyEl.update(content, loadScripts);
29206     },
29207
29208     /**
29209      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29210      * @return {Roo.UpdateManager} The UpdateManager
29211      */
29212     getUpdateManager : function(){
29213         return this.bodyEl.getUpdateManager();
29214     },
29215
29216     /**
29217      * Set a URL to be used to load the content for this TabPanelItem.
29218      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29219      * @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)
29220      * @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)
29221      * @return {Roo.UpdateManager} The UpdateManager
29222      */
29223     setUrl : function(url, params, loadOnce){
29224         if(this.refreshDelegate){
29225             this.un('activate', this.refreshDelegate);
29226         }
29227         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29228         this.on("activate", this.refreshDelegate);
29229         return this.bodyEl.getUpdateManager();
29230     },
29231
29232     /** @private */
29233     _handleRefresh : function(url, params, loadOnce){
29234         if(!loadOnce || !this.loaded){
29235             var updater = this.bodyEl.getUpdateManager();
29236             updater.update(url, params, this._setLoaded.createDelegate(this));
29237         }
29238     },
29239
29240     /**
29241      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29242      *   Will fail silently if the setUrl method has not been called.
29243      *   This does not activate the panel, just updates its content.
29244      */
29245     refresh : function(){
29246         if(this.refreshDelegate){
29247            this.loaded = false;
29248            this.refreshDelegate();
29249         }
29250     },
29251
29252     /** @private */
29253     _setLoaded : function(){
29254         this.loaded = true;
29255     },
29256
29257     /** @private */
29258     closeClick : function(e){
29259         var o = {};
29260         e.stopEvent();
29261         this.fireEvent("beforeclose", this, o);
29262         if(o.cancel !== true){
29263             this.tabPanel.removeTab(this.id);
29264         }
29265     },
29266     /**
29267      * The text displayed in the tooltip for the close icon.
29268      * @type String
29269      */
29270     closeText : "Close this tab"
29271 });
29272
29273 /** @private */
29274 Roo.TabPanel.prototype.createStrip = function(container){
29275     var strip = document.createElement("div");
29276     strip.className = "x-tabs-wrap";
29277     container.appendChild(strip);
29278     return strip;
29279 };
29280 /** @private */
29281 Roo.TabPanel.prototype.createStripList = function(strip){
29282     // div wrapper for retard IE
29283     // returns the "tr" element.
29284     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29285         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29286         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29287     return strip.firstChild.firstChild.firstChild.firstChild;
29288 };
29289 /** @private */
29290 Roo.TabPanel.prototype.createBody = function(container){
29291     var body = document.createElement("div");
29292     Roo.id(body, "tab-body");
29293     Roo.fly(body).addClass("x-tabs-body");
29294     container.appendChild(body);
29295     return body;
29296 };
29297 /** @private */
29298 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29299     var body = Roo.getDom(id);
29300     if(!body){
29301         body = document.createElement("div");
29302         body.id = id;
29303     }
29304     Roo.fly(body).addClass("x-tabs-item-body");
29305     bodyEl.insertBefore(body, bodyEl.firstChild);
29306     return body;
29307 };
29308 /** @private */
29309 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29310     var td = document.createElement("td");
29311     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29312     //stripEl.appendChild(td);
29313     if(closable){
29314         td.className = "x-tabs-closable";
29315         if(!this.closeTpl){
29316             this.closeTpl = new Roo.Template(
29317                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29318                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29319                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29320             );
29321         }
29322         var el = this.closeTpl.overwrite(td, {"text": text});
29323         var close = el.getElementsByTagName("div")[0];
29324         var inner = el.getElementsByTagName("em")[0];
29325         return {"el": el, "close": close, "inner": inner};
29326     } else {
29327         if(!this.tabTpl){
29328             this.tabTpl = new Roo.Template(
29329                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29330                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29331             );
29332         }
29333         var el = this.tabTpl.overwrite(td, {"text": text});
29334         var inner = el.getElementsByTagName("em")[0];
29335         return {"el": el, "inner": inner};
29336     }
29337 };/*
29338  * Based on:
29339  * Ext JS Library 1.1.1
29340  * Copyright(c) 2006-2007, Ext JS, LLC.
29341  *
29342  * Originally Released Under LGPL - original licence link has changed is not relivant.
29343  *
29344  * Fork - LGPL
29345  * <script type="text/javascript">
29346  */
29347
29348 /**
29349  * @class Roo.Button
29350  * @extends Roo.util.Observable
29351  * Simple Button class
29352  * @cfg {String} text The button text
29353  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29354  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29355  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29356  * @cfg {Object} scope The scope of the handler
29357  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29358  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29359  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29360  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29361  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29362  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29363    applies if enableToggle = true)
29364  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29365  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29366   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29367  * @constructor
29368  * Create a new button
29369  * @param {Object} config The config object
29370  */
29371 Roo.Button = function(renderTo, config)
29372 {
29373     if (!config) {
29374         config = renderTo;
29375         renderTo = config.renderTo || false;
29376     }
29377     
29378     Roo.apply(this, config);
29379     this.addEvents({
29380         /**
29381              * @event click
29382              * Fires when this button is clicked
29383              * @param {Button} this
29384              * @param {EventObject} e The click event
29385              */
29386             "click" : true,
29387         /**
29388              * @event toggle
29389              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29390              * @param {Button} this
29391              * @param {Boolean} pressed
29392              */
29393             "toggle" : true,
29394         /**
29395              * @event mouseover
29396              * Fires when the mouse hovers over the button
29397              * @param {Button} this
29398              * @param {Event} e The event object
29399              */
29400         'mouseover' : true,
29401         /**
29402              * @event mouseout
29403              * Fires when the mouse exits the button
29404              * @param {Button} this
29405              * @param {Event} e The event object
29406              */
29407         'mouseout': true,
29408          /**
29409              * @event render
29410              * Fires when the button is rendered
29411              * @param {Button} this
29412              */
29413         'render': true
29414     });
29415     if(this.menu){
29416         this.menu = Roo.menu.MenuMgr.get(this.menu);
29417     }
29418     // register listeners first!!  - so render can be captured..
29419     Roo.util.Observable.call(this);
29420     if(renderTo){
29421         this.render(renderTo);
29422     }
29423     
29424   
29425 };
29426
29427 Roo.extend(Roo.Button, Roo.util.Observable, {
29428     /**
29429      * 
29430      */
29431     
29432     /**
29433      * Read-only. True if this button is hidden
29434      * @type Boolean
29435      */
29436     hidden : false,
29437     /**
29438      * Read-only. True if this button is disabled
29439      * @type Boolean
29440      */
29441     disabled : false,
29442     /**
29443      * Read-only. True if this button is pressed (only if enableToggle = true)
29444      * @type Boolean
29445      */
29446     pressed : false,
29447
29448     /**
29449      * @cfg {Number} tabIndex 
29450      * The DOM tabIndex for this button (defaults to undefined)
29451      */
29452     tabIndex : undefined,
29453
29454     /**
29455      * @cfg {Boolean} enableToggle
29456      * True to enable pressed/not pressed toggling (defaults to false)
29457      */
29458     enableToggle: false,
29459     /**
29460      * @cfg {Mixed} menu
29461      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29462      */
29463     menu : undefined,
29464     /**
29465      * @cfg {String} menuAlign
29466      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29467      */
29468     menuAlign : "tl-bl?",
29469
29470     /**
29471      * @cfg {String} iconCls
29472      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29473      */
29474     iconCls : undefined,
29475     /**
29476      * @cfg {String} type
29477      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29478      */
29479     type : 'button',
29480
29481     // private
29482     menuClassTarget: 'tr',
29483
29484     /**
29485      * @cfg {String} clickEvent
29486      * The type of event to map to the button's event handler (defaults to 'click')
29487      */
29488     clickEvent : 'click',
29489
29490     /**
29491      * @cfg {Boolean} handleMouseEvents
29492      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29493      */
29494     handleMouseEvents : true,
29495
29496     /**
29497      * @cfg {String} tooltipType
29498      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29499      */
29500     tooltipType : 'qtip',
29501
29502     /**
29503      * @cfg {String} cls
29504      * A CSS class to apply to the button's main element.
29505      */
29506     
29507     /**
29508      * @cfg {Roo.Template} template (Optional)
29509      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29510      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29511      * require code modifications if required elements (e.g. a button) aren't present.
29512      */
29513
29514     // private
29515     render : function(renderTo){
29516         var btn;
29517         if(this.hideParent){
29518             this.parentEl = Roo.get(renderTo);
29519         }
29520         if(!this.dhconfig){
29521             if(!this.template){
29522                 if(!Roo.Button.buttonTemplate){
29523                     // hideous table template
29524                     Roo.Button.buttonTemplate = new Roo.Template(
29525                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29526                         '<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>',
29527                         "</tr></tbody></table>");
29528                 }
29529                 this.template = Roo.Button.buttonTemplate;
29530             }
29531             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29532             var btnEl = btn.child("button:first");
29533             btnEl.on('focus', this.onFocus, this);
29534             btnEl.on('blur', this.onBlur, this);
29535             if(this.cls){
29536                 btn.addClass(this.cls);
29537             }
29538             if(this.icon){
29539                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29540             }
29541             if(this.iconCls){
29542                 btnEl.addClass(this.iconCls);
29543                 if(!this.cls){
29544                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29545                 }
29546             }
29547             if(this.tabIndex !== undefined){
29548                 btnEl.dom.tabIndex = this.tabIndex;
29549             }
29550             if(this.tooltip){
29551                 if(typeof this.tooltip == 'object'){
29552                     Roo.QuickTips.tips(Roo.apply({
29553                           target: btnEl.id
29554                     }, this.tooltip));
29555                 } else {
29556                     btnEl.dom[this.tooltipType] = this.tooltip;
29557                 }
29558             }
29559         }else{
29560             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29561         }
29562         this.el = btn;
29563         if(this.id){
29564             this.el.dom.id = this.el.id = this.id;
29565         }
29566         if(this.menu){
29567             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29568             this.menu.on("show", this.onMenuShow, this);
29569             this.menu.on("hide", this.onMenuHide, this);
29570         }
29571         btn.addClass("x-btn");
29572         if(Roo.isIE && !Roo.isIE7){
29573             this.autoWidth.defer(1, this);
29574         }else{
29575             this.autoWidth();
29576         }
29577         if(this.handleMouseEvents){
29578             btn.on("mouseover", this.onMouseOver, this);
29579             btn.on("mouseout", this.onMouseOut, this);
29580             btn.on("mousedown", this.onMouseDown, this);
29581         }
29582         btn.on(this.clickEvent, this.onClick, this);
29583         //btn.on("mouseup", this.onMouseUp, this);
29584         if(this.hidden){
29585             this.hide();
29586         }
29587         if(this.disabled){
29588             this.disable();
29589         }
29590         Roo.ButtonToggleMgr.register(this);
29591         if(this.pressed){
29592             this.el.addClass("x-btn-pressed");
29593         }
29594         if(this.repeat){
29595             var repeater = new Roo.util.ClickRepeater(btn,
29596                 typeof this.repeat == "object" ? this.repeat : {}
29597             );
29598             repeater.on("click", this.onClick,  this);
29599         }
29600         
29601         this.fireEvent('render', this);
29602         
29603     },
29604     /**
29605      * Returns the button's underlying element
29606      * @return {Roo.Element} The element
29607      */
29608     getEl : function(){
29609         return this.el;  
29610     },
29611     
29612     /**
29613      * Destroys this Button and removes any listeners.
29614      */
29615     destroy : function(){
29616         Roo.ButtonToggleMgr.unregister(this);
29617         this.el.removeAllListeners();
29618         this.purgeListeners();
29619         this.el.remove();
29620     },
29621
29622     // private
29623     autoWidth : function(){
29624         if(this.el){
29625             this.el.setWidth("auto");
29626             if(Roo.isIE7 && Roo.isStrict){
29627                 var ib = this.el.child('button');
29628                 if(ib && ib.getWidth() > 20){
29629                     ib.clip();
29630                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29631                 }
29632             }
29633             if(this.minWidth){
29634                 if(this.hidden){
29635                     this.el.beginMeasure();
29636                 }
29637                 if(this.el.getWidth() < this.minWidth){
29638                     this.el.setWidth(this.minWidth);
29639                 }
29640                 if(this.hidden){
29641                     this.el.endMeasure();
29642                 }
29643             }
29644         }
29645     },
29646
29647     /**
29648      * Assigns this button's click handler
29649      * @param {Function} handler The function to call when the button is clicked
29650      * @param {Object} scope (optional) Scope for the function passed in
29651      */
29652     setHandler : function(handler, scope){
29653         this.handler = handler;
29654         this.scope = scope;  
29655     },
29656     
29657     /**
29658      * Sets this button's text
29659      * @param {String} text The button text
29660      */
29661     setText : function(text){
29662         this.text = text;
29663         if(this.el){
29664             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29665         }
29666         this.autoWidth();
29667     },
29668     
29669     /**
29670      * Gets the text for this button
29671      * @return {String} The button text
29672      */
29673     getText : function(){
29674         return this.text;  
29675     },
29676     
29677     /**
29678      * Show this button
29679      */
29680     show: function(){
29681         this.hidden = false;
29682         if(this.el){
29683             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29684         }
29685     },
29686     
29687     /**
29688      * Hide this button
29689      */
29690     hide: function(){
29691         this.hidden = true;
29692         if(this.el){
29693             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29694         }
29695     },
29696     
29697     /**
29698      * Convenience function for boolean show/hide
29699      * @param {Boolean} visible True to show, false to hide
29700      */
29701     setVisible: function(visible){
29702         if(visible) {
29703             this.show();
29704         }else{
29705             this.hide();
29706         }
29707     },
29708     
29709     /**
29710      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29711      * @param {Boolean} state (optional) Force a particular state
29712      */
29713     toggle : function(state){
29714         state = state === undefined ? !this.pressed : state;
29715         if(state != this.pressed){
29716             if(state){
29717                 this.el.addClass("x-btn-pressed");
29718                 this.pressed = true;
29719                 this.fireEvent("toggle", this, true);
29720             }else{
29721                 this.el.removeClass("x-btn-pressed");
29722                 this.pressed = false;
29723                 this.fireEvent("toggle", this, false);
29724             }
29725             if(this.toggleHandler){
29726                 this.toggleHandler.call(this.scope || this, this, state);
29727             }
29728         }
29729     },
29730     
29731     /**
29732      * Focus the button
29733      */
29734     focus : function(){
29735         this.el.child('button:first').focus();
29736     },
29737     
29738     /**
29739      * Disable this button
29740      */
29741     disable : function(){
29742         if(this.el){
29743             this.el.addClass("x-btn-disabled");
29744         }
29745         this.disabled = true;
29746     },
29747     
29748     /**
29749      * Enable this button
29750      */
29751     enable : function(){
29752         if(this.el){
29753             this.el.removeClass("x-btn-disabled");
29754         }
29755         this.disabled = false;
29756     },
29757
29758     /**
29759      * Convenience function for boolean enable/disable
29760      * @param {Boolean} enabled True to enable, false to disable
29761      */
29762     setDisabled : function(v){
29763         this[v !== true ? "enable" : "disable"]();
29764     },
29765
29766     // private
29767     onClick : function(e)
29768     {
29769         if(e){
29770             e.preventDefault();
29771         }
29772         if(e.button != 0){
29773             return;
29774         }
29775         if(!this.disabled){
29776             if(this.enableToggle){
29777                 this.toggle();
29778             }
29779             if(this.menu && !this.menu.isVisible()){
29780                 this.menu.show(this.el, this.menuAlign);
29781             }
29782             this.fireEvent("click", this, e);
29783             if(this.handler){
29784                 this.el.removeClass("x-btn-over");
29785                 this.handler.call(this.scope || this, this, e);
29786             }
29787         }
29788     },
29789     // private
29790     onMouseOver : function(e){
29791         if(!this.disabled){
29792             this.el.addClass("x-btn-over");
29793             this.fireEvent('mouseover', this, e);
29794         }
29795     },
29796     // private
29797     onMouseOut : function(e){
29798         if(!e.within(this.el,  true)){
29799             this.el.removeClass("x-btn-over");
29800             this.fireEvent('mouseout', this, e);
29801         }
29802     },
29803     // private
29804     onFocus : function(e){
29805         if(!this.disabled){
29806             this.el.addClass("x-btn-focus");
29807         }
29808     },
29809     // private
29810     onBlur : function(e){
29811         this.el.removeClass("x-btn-focus");
29812     },
29813     // private
29814     onMouseDown : function(e){
29815         if(!this.disabled && e.button == 0){
29816             this.el.addClass("x-btn-click");
29817             Roo.get(document).on('mouseup', this.onMouseUp, this);
29818         }
29819     },
29820     // private
29821     onMouseUp : function(e){
29822         if(e.button == 0){
29823             this.el.removeClass("x-btn-click");
29824             Roo.get(document).un('mouseup', this.onMouseUp, this);
29825         }
29826     },
29827     // private
29828     onMenuShow : function(e){
29829         this.el.addClass("x-btn-menu-active");
29830     },
29831     // private
29832     onMenuHide : function(e){
29833         this.el.removeClass("x-btn-menu-active");
29834     }   
29835 });
29836
29837 // Private utility class used by Button
29838 Roo.ButtonToggleMgr = function(){
29839    var groups = {};
29840    
29841    function toggleGroup(btn, state){
29842        if(state){
29843            var g = groups[btn.toggleGroup];
29844            for(var i = 0, l = g.length; i < l; i++){
29845                if(g[i] != btn){
29846                    g[i].toggle(false);
29847                }
29848            }
29849        }
29850    }
29851    
29852    return {
29853        register : function(btn){
29854            if(!btn.toggleGroup){
29855                return;
29856            }
29857            var g = groups[btn.toggleGroup];
29858            if(!g){
29859                g = groups[btn.toggleGroup] = [];
29860            }
29861            g.push(btn);
29862            btn.on("toggle", toggleGroup);
29863        },
29864        
29865        unregister : function(btn){
29866            if(!btn.toggleGroup){
29867                return;
29868            }
29869            var g = groups[btn.toggleGroup];
29870            if(g){
29871                g.remove(btn);
29872                btn.un("toggle", toggleGroup);
29873            }
29874        }
29875    };
29876 }();/*
29877  * Based on:
29878  * Ext JS Library 1.1.1
29879  * Copyright(c) 2006-2007, Ext JS, LLC.
29880  *
29881  * Originally Released Under LGPL - original licence link has changed is not relivant.
29882  *
29883  * Fork - LGPL
29884  * <script type="text/javascript">
29885  */
29886  
29887 /**
29888  * @class Roo.SplitButton
29889  * @extends Roo.Button
29890  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29891  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29892  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29893  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29894  * @cfg {String} arrowTooltip The title attribute of the arrow
29895  * @constructor
29896  * Create a new menu button
29897  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29898  * @param {Object} config The config object
29899  */
29900 Roo.SplitButton = function(renderTo, config){
29901     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29902     /**
29903      * @event arrowclick
29904      * Fires when this button's arrow is clicked
29905      * @param {SplitButton} this
29906      * @param {EventObject} e The click event
29907      */
29908     this.addEvents({"arrowclick":true});
29909 };
29910
29911 Roo.extend(Roo.SplitButton, Roo.Button, {
29912     render : function(renderTo){
29913         // this is one sweet looking template!
29914         var tpl = new Roo.Template(
29915             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29916             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29917             '<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>',
29918             "</tbody></table></td><td>",
29919             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29920             '<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>',
29921             "</tbody></table></td></tr></table>"
29922         );
29923         var btn = tpl.append(renderTo, [this.text, this.type], true);
29924         var btnEl = btn.child("button");
29925         if(this.cls){
29926             btn.addClass(this.cls);
29927         }
29928         if(this.icon){
29929             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29930         }
29931         if(this.iconCls){
29932             btnEl.addClass(this.iconCls);
29933             if(!this.cls){
29934                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29935             }
29936         }
29937         this.el = btn;
29938         if(this.handleMouseEvents){
29939             btn.on("mouseover", this.onMouseOver, this);
29940             btn.on("mouseout", this.onMouseOut, this);
29941             btn.on("mousedown", this.onMouseDown, this);
29942             btn.on("mouseup", this.onMouseUp, this);
29943         }
29944         btn.on(this.clickEvent, this.onClick, this);
29945         if(this.tooltip){
29946             if(typeof this.tooltip == 'object'){
29947                 Roo.QuickTips.tips(Roo.apply({
29948                       target: btnEl.id
29949                 }, this.tooltip));
29950             } else {
29951                 btnEl.dom[this.tooltipType] = this.tooltip;
29952             }
29953         }
29954         if(this.arrowTooltip){
29955             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29956         }
29957         if(this.hidden){
29958             this.hide();
29959         }
29960         if(this.disabled){
29961             this.disable();
29962         }
29963         if(this.pressed){
29964             this.el.addClass("x-btn-pressed");
29965         }
29966         if(Roo.isIE && !Roo.isIE7){
29967             this.autoWidth.defer(1, this);
29968         }else{
29969             this.autoWidth();
29970         }
29971         if(this.menu){
29972             this.menu.on("show", this.onMenuShow, this);
29973             this.menu.on("hide", this.onMenuHide, this);
29974         }
29975         this.fireEvent('render', this);
29976     },
29977
29978     // private
29979     autoWidth : function(){
29980         if(this.el){
29981             var tbl = this.el.child("table:first");
29982             var tbl2 = this.el.child("table:last");
29983             this.el.setWidth("auto");
29984             tbl.setWidth("auto");
29985             if(Roo.isIE7 && Roo.isStrict){
29986                 var ib = this.el.child('button:first');
29987                 if(ib && ib.getWidth() > 20){
29988                     ib.clip();
29989                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29990                 }
29991             }
29992             if(this.minWidth){
29993                 if(this.hidden){
29994                     this.el.beginMeasure();
29995                 }
29996                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29997                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29998                 }
29999                 if(this.hidden){
30000                     this.el.endMeasure();
30001                 }
30002             }
30003             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30004         } 
30005     },
30006     /**
30007      * Sets this button's click handler
30008      * @param {Function} handler The function to call when the button is clicked
30009      * @param {Object} scope (optional) Scope for the function passed above
30010      */
30011     setHandler : function(handler, scope){
30012         this.handler = handler;
30013         this.scope = scope;  
30014     },
30015     
30016     /**
30017      * Sets this button's arrow click handler
30018      * @param {Function} handler The function to call when the arrow is clicked
30019      * @param {Object} scope (optional) Scope for the function passed above
30020      */
30021     setArrowHandler : function(handler, scope){
30022         this.arrowHandler = handler;
30023         this.scope = scope;  
30024     },
30025     
30026     /**
30027      * Focus the button
30028      */
30029     focus : function(){
30030         if(this.el){
30031             this.el.child("button:first").focus();
30032         }
30033     },
30034
30035     // private
30036     onClick : function(e){
30037         e.preventDefault();
30038         if(!this.disabled){
30039             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30040                 if(this.menu && !this.menu.isVisible()){
30041                     this.menu.show(this.el, this.menuAlign);
30042                 }
30043                 this.fireEvent("arrowclick", this, e);
30044                 if(this.arrowHandler){
30045                     this.arrowHandler.call(this.scope || this, this, e);
30046                 }
30047             }else{
30048                 this.fireEvent("click", this, e);
30049                 if(this.handler){
30050                     this.handler.call(this.scope || this, this, e);
30051                 }
30052             }
30053         }
30054     },
30055     // private
30056     onMouseDown : function(e){
30057         if(!this.disabled){
30058             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30059         }
30060     },
30061     // private
30062     onMouseUp : function(e){
30063         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30064     }   
30065 });
30066
30067
30068 // backwards compat
30069 Roo.MenuButton = Roo.SplitButton;/*
30070  * Based on:
30071  * Ext JS Library 1.1.1
30072  * Copyright(c) 2006-2007, Ext JS, LLC.
30073  *
30074  * Originally Released Under LGPL - original licence link has changed is not relivant.
30075  *
30076  * Fork - LGPL
30077  * <script type="text/javascript">
30078  */
30079
30080 /**
30081  * @class Roo.Toolbar
30082  * Basic Toolbar class.
30083  * @constructor
30084  * Creates a new Toolbar
30085  * @param {Object} container The config object
30086  */ 
30087 Roo.Toolbar = function(container, buttons, config)
30088 {
30089     /// old consturctor format still supported..
30090     if(container instanceof Array){ // omit the container for later rendering
30091         buttons = container;
30092         config = buttons;
30093         container = null;
30094     }
30095     if (typeof(container) == 'object' && container.xtype) {
30096         config = container;
30097         container = config.container;
30098         buttons = config.buttons || []; // not really - use items!!
30099     }
30100     var xitems = [];
30101     if (config && config.items) {
30102         xitems = config.items;
30103         delete config.items;
30104     }
30105     Roo.apply(this, config);
30106     this.buttons = buttons;
30107     
30108     if(container){
30109         this.render(container);
30110     }
30111     this.xitems = xitems;
30112     Roo.each(xitems, function(b) {
30113         this.add(b);
30114     }, this);
30115     
30116 };
30117
30118 Roo.Toolbar.prototype = {
30119     /**
30120      * @cfg {Array} items
30121      * array of button configs or elements to add (will be converted to a MixedCollection)
30122      */
30123     
30124     /**
30125      * @cfg {String/HTMLElement/Element} container
30126      * The id or element that will contain the toolbar
30127      */
30128     // private
30129     render : function(ct){
30130         this.el = Roo.get(ct);
30131         if(this.cls){
30132             this.el.addClass(this.cls);
30133         }
30134         // using a table allows for vertical alignment
30135         // 100% width is needed by Safari...
30136         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30137         this.tr = this.el.child("tr", true);
30138         var autoId = 0;
30139         this.items = new Roo.util.MixedCollection(false, function(o){
30140             return o.id || ("item" + (++autoId));
30141         });
30142         if(this.buttons){
30143             this.add.apply(this, this.buttons);
30144             delete this.buttons;
30145         }
30146     },
30147
30148     /**
30149      * Adds element(s) to the toolbar -- this function takes a variable number of 
30150      * arguments of mixed type and adds them to the toolbar.
30151      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30152      * <ul>
30153      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30154      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30155      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30156      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30157      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30158      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30159      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30160      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30161      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30162      * </ul>
30163      * @param {Mixed} arg2
30164      * @param {Mixed} etc.
30165      */
30166     add : function(){
30167         var a = arguments, l = a.length;
30168         for(var i = 0; i < l; i++){
30169             this._add(a[i]);
30170         }
30171     },
30172     // private..
30173     _add : function(el) {
30174         
30175         if (el.xtype) {
30176             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30177         }
30178         
30179         if (el.applyTo){ // some kind of form field
30180             return this.addField(el);
30181         } 
30182         if (el.render){ // some kind of Toolbar.Item
30183             return this.addItem(el);
30184         }
30185         if (typeof el == "string"){ // string
30186             if(el == "separator" || el == "-"){
30187                 return this.addSeparator();
30188             }
30189             if (el == " "){
30190                 return this.addSpacer();
30191             }
30192             if(el == "->"){
30193                 return this.addFill();
30194             }
30195             return this.addText(el);
30196             
30197         }
30198         if(el.tagName){ // element
30199             return this.addElement(el);
30200         }
30201         if(typeof el == "object"){ // must be button config?
30202             return this.addButton(el);
30203         }
30204         // and now what?!?!
30205         return false;
30206         
30207     },
30208     
30209     /**
30210      * Add an Xtype element
30211      * @param {Object} xtype Xtype Object
30212      * @return {Object} created Object
30213      */
30214     addxtype : function(e){
30215         return this.add(e);  
30216     },
30217     
30218     /**
30219      * Returns the Element for this toolbar.
30220      * @return {Roo.Element}
30221      */
30222     getEl : function(){
30223         return this.el;  
30224     },
30225     
30226     /**
30227      * Adds a separator
30228      * @return {Roo.Toolbar.Item} The separator item
30229      */
30230     addSeparator : function(){
30231         return this.addItem(new Roo.Toolbar.Separator());
30232     },
30233
30234     /**
30235      * Adds a spacer element
30236      * @return {Roo.Toolbar.Spacer} The spacer item
30237      */
30238     addSpacer : function(){
30239         return this.addItem(new Roo.Toolbar.Spacer());
30240     },
30241
30242     /**
30243      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30244      * @return {Roo.Toolbar.Fill} The fill item
30245      */
30246     addFill : function(){
30247         return this.addItem(new Roo.Toolbar.Fill());
30248     },
30249
30250     /**
30251      * Adds any standard HTML element to the toolbar
30252      * @param {String/HTMLElement/Element} el The element or id of the element to add
30253      * @return {Roo.Toolbar.Item} The element's item
30254      */
30255     addElement : function(el){
30256         return this.addItem(new Roo.Toolbar.Item(el));
30257     },
30258     /**
30259      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30260      * @type Roo.util.MixedCollection  
30261      */
30262     items : false,
30263      
30264     /**
30265      * Adds any Toolbar.Item or subclass
30266      * @param {Roo.Toolbar.Item} item
30267      * @return {Roo.Toolbar.Item} The item
30268      */
30269     addItem : function(item){
30270         var td = this.nextBlock();
30271         item.render(td);
30272         this.items.add(item);
30273         return item;
30274     },
30275     
30276     /**
30277      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30278      * @param {Object/Array} config A button config or array of configs
30279      * @return {Roo.Toolbar.Button/Array}
30280      */
30281     addButton : function(config){
30282         if(config instanceof Array){
30283             var buttons = [];
30284             for(var i = 0, len = config.length; i < len; i++) {
30285                 buttons.push(this.addButton(config[i]));
30286             }
30287             return buttons;
30288         }
30289         var b = config;
30290         if(!(config instanceof Roo.Toolbar.Button)){
30291             b = config.split ?
30292                 new Roo.Toolbar.SplitButton(config) :
30293                 new Roo.Toolbar.Button(config);
30294         }
30295         var td = this.nextBlock();
30296         b.render(td);
30297         this.items.add(b);
30298         return b;
30299     },
30300     
30301     /**
30302      * Adds text to the toolbar
30303      * @param {String} text The text to add
30304      * @return {Roo.Toolbar.Item} The element's item
30305      */
30306     addText : function(text){
30307         return this.addItem(new Roo.Toolbar.TextItem(text));
30308     },
30309     
30310     /**
30311      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30312      * @param {Number} index The index where the item is to be inserted
30313      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30314      * @return {Roo.Toolbar.Button/Item}
30315      */
30316     insertButton : function(index, item){
30317         if(item instanceof Array){
30318             var buttons = [];
30319             for(var i = 0, len = item.length; i < len; i++) {
30320                buttons.push(this.insertButton(index + i, item[i]));
30321             }
30322             return buttons;
30323         }
30324         if (!(item instanceof Roo.Toolbar.Button)){
30325            item = new Roo.Toolbar.Button(item);
30326         }
30327         var td = document.createElement("td");
30328         this.tr.insertBefore(td, this.tr.childNodes[index]);
30329         item.render(td);
30330         this.items.insert(index, item);
30331         return item;
30332     },
30333     
30334     /**
30335      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30336      * @param {Object} config
30337      * @return {Roo.Toolbar.Item} The element's item
30338      */
30339     addDom : function(config, returnEl){
30340         var td = this.nextBlock();
30341         Roo.DomHelper.overwrite(td, config);
30342         var ti = new Roo.Toolbar.Item(td.firstChild);
30343         ti.render(td);
30344         this.items.add(ti);
30345         return ti;
30346     },
30347
30348     /**
30349      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30350      * @type Roo.util.MixedCollection  
30351      */
30352     fields : false,
30353     
30354     /**
30355      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30356      * Note: the field should not have been rendered yet. For a field that has already been
30357      * rendered, use {@link #addElement}.
30358      * @param {Roo.form.Field} field
30359      * @return {Roo.ToolbarItem}
30360      */
30361      
30362       
30363     addField : function(field) {
30364         if (!this.fields) {
30365             var autoId = 0;
30366             this.fields = new Roo.util.MixedCollection(false, function(o){
30367                 return o.id || ("item" + (++autoId));
30368             });
30369
30370         }
30371         
30372         var td = this.nextBlock();
30373         field.render(td);
30374         var ti = new Roo.Toolbar.Item(td.firstChild);
30375         ti.render(td);
30376         this.items.add(ti);
30377         this.fields.add(field);
30378         return ti;
30379     },
30380     /**
30381      * Hide the toolbar
30382      * @method hide
30383      */
30384      
30385       
30386     hide : function()
30387     {
30388         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30389         this.el.child('div').hide();
30390     },
30391     /**
30392      * Show the toolbar
30393      * @method show
30394      */
30395     show : function()
30396     {
30397         this.el.child('div').show();
30398     },
30399       
30400     // private
30401     nextBlock : function(){
30402         var td = document.createElement("td");
30403         this.tr.appendChild(td);
30404         return td;
30405     },
30406
30407     // private
30408     destroy : function(){
30409         if(this.items){ // rendered?
30410             Roo.destroy.apply(Roo, this.items.items);
30411         }
30412         if(this.fields){ // rendered?
30413             Roo.destroy.apply(Roo, this.fields.items);
30414         }
30415         Roo.Element.uncache(this.el, this.tr);
30416     }
30417 };
30418
30419 /**
30420  * @class Roo.Toolbar.Item
30421  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30422  * @constructor
30423  * Creates a new Item
30424  * @param {HTMLElement} el 
30425  */
30426 Roo.Toolbar.Item = function(el){
30427     var cfg = {};
30428     if (typeof (el.xtype) != 'undefined') {
30429         cfg = el;
30430         el = cfg.el;
30431     }
30432     
30433     this.el = Roo.getDom(el);
30434     this.id = Roo.id(this.el);
30435     this.hidden = false;
30436     
30437     this.addEvents({
30438          /**
30439              * @event render
30440              * Fires when the button is rendered
30441              * @param {Button} this
30442              */
30443         'render': true
30444     });
30445     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30446 };
30447 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30448 //Roo.Toolbar.Item.prototype = {
30449     
30450     /**
30451      * Get this item's HTML Element
30452      * @return {HTMLElement}
30453      */
30454     getEl : function(){
30455        return this.el;  
30456     },
30457
30458     // private
30459     render : function(td){
30460         
30461          this.td = td;
30462         td.appendChild(this.el);
30463         
30464         this.fireEvent('render', this);
30465     },
30466     
30467     /**
30468      * Removes and destroys this item.
30469      */
30470     destroy : function(){
30471         this.td.parentNode.removeChild(this.td);
30472     },
30473     
30474     /**
30475      * Shows this item.
30476      */
30477     show: function(){
30478         this.hidden = false;
30479         this.td.style.display = "";
30480     },
30481     
30482     /**
30483      * Hides this item.
30484      */
30485     hide: function(){
30486         this.hidden = true;
30487         this.td.style.display = "none";
30488     },
30489     
30490     /**
30491      * Convenience function for boolean show/hide.
30492      * @param {Boolean} visible true to show/false to hide
30493      */
30494     setVisible: function(visible){
30495         if(visible) {
30496             this.show();
30497         }else{
30498             this.hide();
30499         }
30500     },
30501     
30502     /**
30503      * Try to focus this item.
30504      */
30505     focus : function(){
30506         Roo.fly(this.el).focus();
30507     },
30508     
30509     /**
30510      * Disables this item.
30511      */
30512     disable : function(){
30513         Roo.fly(this.td).addClass("x-item-disabled");
30514         this.disabled = true;
30515         this.el.disabled = true;
30516     },
30517     
30518     /**
30519      * Enables this item.
30520      */
30521     enable : function(){
30522         Roo.fly(this.td).removeClass("x-item-disabled");
30523         this.disabled = false;
30524         this.el.disabled = false;
30525     }
30526 });
30527
30528
30529 /**
30530  * @class Roo.Toolbar.Separator
30531  * @extends Roo.Toolbar.Item
30532  * A simple toolbar separator class
30533  * @constructor
30534  * Creates a new Separator
30535  */
30536 Roo.Toolbar.Separator = function(cfg){
30537     
30538     var s = document.createElement("span");
30539     s.className = "ytb-sep";
30540     if (cfg) {
30541         cfg.el = s;
30542     }
30543     
30544     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30545 };
30546 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30547     enable:Roo.emptyFn,
30548     disable:Roo.emptyFn,
30549     focus:Roo.emptyFn
30550 });
30551
30552 /**
30553  * @class Roo.Toolbar.Spacer
30554  * @extends Roo.Toolbar.Item
30555  * A simple element that adds extra horizontal space to a toolbar.
30556  * @constructor
30557  * Creates a new Spacer
30558  */
30559 Roo.Toolbar.Spacer = function(cfg){
30560     var s = document.createElement("div");
30561     s.className = "ytb-spacer";
30562     if (cfg) {
30563         cfg.el = s;
30564     }
30565     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30566 };
30567 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30568     enable:Roo.emptyFn,
30569     disable:Roo.emptyFn,
30570     focus:Roo.emptyFn
30571 });
30572
30573 /**
30574  * @class Roo.Toolbar.Fill
30575  * @extends Roo.Toolbar.Spacer
30576  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30577  * @constructor
30578  * Creates a new Spacer
30579  */
30580 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30581     // private
30582     render : function(td){
30583         td.style.width = '100%';
30584         Roo.Toolbar.Fill.superclass.render.call(this, td);
30585     }
30586 });
30587
30588 /**
30589  * @class Roo.Toolbar.TextItem
30590  * @extends Roo.Toolbar.Item
30591  * A simple class that renders text directly into a toolbar.
30592  * @constructor
30593  * Creates a new TextItem
30594  * @cfg {string} text 
30595  */
30596 Roo.Toolbar.TextItem = function(cfg){
30597     var  text = cfg || "";
30598     if (typeof(cfg) == 'object') {
30599         text = cfg.text || "";
30600     }  else {
30601         cfg = null;
30602     }
30603     var s = document.createElement("span");
30604     s.className = "ytb-text";
30605     s.innerHTML = text;
30606     if (cfg) {
30607         cfg.el  = s;
30608     }
30609     
30610     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30611 };
30612 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30613     
30614      
30615     enable:Roo.emptyFn,
30616     disable:Roo.emptyFn,
30617     focus:Roo.emptyFn
30618 });
30619
30620 /**
30621  * @class Roo.Toolbar.Button
30622  * @extends Roo.Button
30623  * A button that renders into a toolbar.
30624  * @constructor
30625  * Creates a new Button
30626  * @param {Object} config A standard {@link Roo.Button} config object
30627  */
30628 Roo.Toolbar.Button = function(config){
30629     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30630 };
30631 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30632     render : function(td){
30633         this.td = td;
30634         Roo.Toolbar.Button.superclass.render.call(this, td);
30635     },
30636     
30637     /**
30638      * Removes and destroys this button
30639      */
30640     destroy : function(){
30641         Roo.Toolbar.Button.superclass.destroy.call(this);
30642         this.td.parentNode.removeChild(this.td);
30643     },
30644     
30645     /**
30646      * Shows this button
30647      */
30648     show: function(){
30649         this.hidden = false;
30650         this.td.style.display = "";
30651     },
30652     
30653     /**
30654      * Hides this button
30655      */
30656     hide: function(){
30657         this.hidden = true;
30658         this.td.style.display = "none";
30659     },
30660
30661     /**
30662      * Disables this item
30663      */
30664     disable : function(){
30665         Roo.fly(this.td).addClass("x-item-disabled");
30666         this.disabled = true;
30667     },
30668
30669     /**
30670      * Enables this item
30671      */
30672     enable : function(){
30673         Roo.fly(this.td).removeClass("x-item-disabled");
30674         this.disabled = false;
30675     }
30676 });
30677 // backwards compat
30678 Roo.ToolbarButton = Roo.Toolbar.Button;
30679
30680 /**
30681  * @class Roo.Toolbar.SplitButton
30682  * @extends Roo.SplitButton
30683  * A menu button that renders into a toolbar.
30684  * @constructor
30685  * Creates a new SplitButton
30686  * @param {Object} config A standard {@link Roo.SplitButton} config object
30687  */
30688 Roo.Toolbar.SplitButton = function(config){
30689     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30690 };
30691 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30692     render : function(td){
30693         this.td = td;
30694         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30695     },
30696     
30697     /**
30698      * Removes and destroys this button
30699      */
30700     destroy : function(){
30701         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30702         this.td.parentNode.removeChild(this.td);
30703     },
30704     
30705     /**
30706      * Shows this button
30707      */
30708     show: function(){
30709         this.hidden = false;
30710         this.td.style.display = "";
30711     },
30712     
30713     /**
30714      * Hides this button
30715      */
30716     hide: function(){
30717         this.hidden = true;
30718         this.td.style.display = "none";
30719     }
30720 });
30721
30722 // backwards compat
30723 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30724  * Based on:
30725  * Ext JS Library 1.1.1
30726  * Copyright(c) 2006-2007, Ext JS, LLC.
30727  *
30728  * Originally Released Under LGPL - original licence link has changed is not relivant.
30729  *
30730  * Fork - LGPL
30731  * <script type="text/javascript">
30732  */
30733  
30734 /**
30735  * @class Roo.PagingToolbar
30736  * @extends Roo.Toolbar
30737  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30738  * @constructor
30739  * Create a new PagingToolbar
30740  * @param {Object} config The config object
30741  */
30742 Roo.PagingToolbar = function(el, ds, config)
30743 {
30744     // old args format still supported... - xtype is prefered..
30745     if (typeof(el) == 'object' && el.xtype) {
30746         // created from xtype...
30747         config = el;
30748         ds = el.dataSource;
30749         el = config.container;
30750     }
30751     var items = [];
30752     if (config.items) {
30753         items = config.items;
30754         config.items = [];
30755     }
30756     
30757     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30758     this.ds = ds;
30759     this.cursor = 0;
30760     this.renderButtons(this.el);
30761     this.bind(ds);
30762     
30763     // supprot items array.
30764    
30765     Roo.each(items, function(e) {
30766         this.add(Roo.factory(e));
30767     },this);
30768     
30769 };
30770
30771 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30772     /**
30773      * @cfg {Roo.data.Store} dataSource
30774      * The underlying data store providing the paged data
30775      */
30776     /**
30777      * @cfg {String/HTMLElement/Element} container
30778      * container The id or element that will contain the toolbar
30779      */
30780     /**
30781      * @cfg {Boolean} displayInfo
30782      * True to display the displayMsg (defaults to false)
30783      */
30784     /**
30785      * @cfg {Number} pageSize
30786      * The number of records to display per page (defaults to 20)
30787      */
30788     pageSize: 20,
30789     /**
30790      * @cfg {String} displayMsg
30791      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30792      */
30793     displayMsg : 'Displaying {0} - {1} of {2}',
30794     /**
30795      * @cfg {String} emptyMsg
30796      * The message to display when no records are found (defaults to "No data to display")
30797      */
30798     emptyMsg : 'No data to display',
30799     /**
30800      * Customizable piece of the default paging text (defaults to "Page")
30801      * @type String
30802      */
30803     beforePageText : "Page",
30804     /**
30805      * Customizable piece of the default paging text (defaults to "of %0")
30806      * @type String
30807      */
30808     afterPageText : "of {0}",
30809     /**
30810      * Customizable piece of the default paging text (defaults to "First Page")
30811      * @type String
30812      */
30813     firstText : "First Page",
30814     /**
30815      * Customizable piece of the default paging text (defaults to "Previous Page")
30816      * @type String
30817      */
30818     prevText : "Previous Page",
30819     /**
30820      * Customizable piece of the default paging text (defaults to "Next Page")
30821      * @type String
30822      */
30823     nextText : "Next Page",
30824     /**
30825      * Customizable piece of the default paging text (defaults to "Last Page")
30826      * @type String
30827      */
30828     lastText : "Last Page",
30829     /**
30830      * Customizable piece of the default paging text (defaults to "Refresh")
30831      * @type String
30832      */
30833     refreshText : "Refresh",
30834
30835     // private
30836     renderButtons : function(el){
30837         Roo.PagingToolbar.superclass.render.call(this, el);
30838         this.first = this.addButton({
30839             tooltip: this.firstText,
30840             cls: "x-btn-icon x-grid-page-first",
30841             disabled: true,
30842             handler: this.onClick.createDelegate(this, ["first"])
30843         });
30844         this.prev = this.addButton({
30845             tooltip: this.prevText,
30846             cls: "x-btn-icon x-grid-page-prev",
30847             disabled: true,
30848             handler: this.onClick.createDelegate(this, ["prev"])
30849         });
30850         //this.addSeparator();
30851         this.add(this.beforePageText);
30852         this.field = Roo.get(this.addDom({
30853            tag: "input",
30854            type: "text",
30855            size: "3",
30856            value: "1",
30857            cls: "x-grid-page-number"
30858         }).el);
30859         this.field.on("keydown", this.onPagingKeydown, this);
30860         this.field.on("focus", function(){this.dom.select();});
30861         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30862         this.field.setHeight(18);
30863         //this.addSeparator();
30864         this.next = this.addButton({
30865             tooltip: this.nextText,
30866             cls: "x-btn-icon x-grid-page-next",
30867             disabled: true,
30868             handler: this.onClick.createDelegate(this, ["next"])
30869         });
30870         this.last = this.addButton({
30871             tooltip: this.lastText,
30872             cls: "x-btn-icon x-grid-page-last",
30873             disabled: true,
30874             handler: this.onClick.createDelegate(this, ["last"])
30875         });
30876         //this.addSeparator();
30877         this.loading = this.addButton({
30878             tooltip: this.refreshText,
30879             cls: "x-btn-icon x-grid-loading",
30880             handler: this.onClick.createDelegate(this, ["refresh"])
30881         });
30882
30883         if(this.displayInfo){
30884             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30885         }
30886     },
30887
30888     // private
30889     updateInfo : function(){
30890         if(this.displayEl){
30891             var count = this.ds.getCount();
30892             var msg = count == 0 ?
30893                 this.emptyMsg :
30894                 String.format(
30895                     this.displayMsg,
30896                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30897                 );
30898             this.displayEl.update(msg);
30899         }
30900     },
30901
30902     // private
30903     onLoad : function(ds, r, o){
30904        this.cursor = o.params ? o.params.start : 0;
30905        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30906
30907        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30908        this.field.dom.value = ap;
30909        this.first.setDisabled(ap == 1);
30910        this.prev.setDisabled(ap == 1);
30911        this.next.setDisabled(ap == ps);
30912        this.last.setDisabled(ap == ps);
30913        this.loading.enable();
30914        this.updateInfo();
30915     },
30916
30917     // private
30918     getPageData : function(){
30919         var total = this.ds.getTotalCount();
30920         return {
30921             total : total,
30922             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30923             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30924         };
30925     },
30926
30927     // private
30928     onLoadError : function(){
30929         this.loading.enable();
30930     },
30931
30932     // private
30933     onPagingKeydown : function(e){
30934         var k = e.getKey();
30935         var d = this.getPageData();
30936         if(k == e.RETURN){
30937             var v = this.field.dom.value, pageNum;
30938             if(!v || isNaN(pageNum = parseInt(v, 10))){
30939                 this.field.dom.value = d.activePage;
30940                 return;
30941             }
30942             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30943             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30944             e.stopEvent();
30945         }
30946         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))
30947         {
30948           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30949           this.field.dom.value = pageNum;
30950           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30951           e.stopEvent();
30952         }
30953         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30954         {
30955           var v = this.field.dom.value, pageNum; 
30956           var increment = (e.shiftKey) ? 10 : 1;
30957           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30958             increment *= -1;
30959           }
30960           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30961             this.field.dom.value = d.activePage;
30962             return;
30963           }
30964           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30965           {
30966             this.field.dom.value = parseInt(v, 10) + increment;
30967             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30968             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30969           }
30970           e.stopEvent();
30971         }
30972     },
30973
30974     // private
30975     beforeLoad : function(){
30976         if(this.loading){
30977             this.loading.disable();
30978         }
30979     },
30980
30981     // private
30982     onClick : function(which){
30983         var ds = this.ds;
30984         switch(which){
30985             case "first":
30986                 ds.load({params:{start: 0, limit: this.pageSize}});
30987             break;
30988             case "prev":
30989                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30990             break;
30991             case "next":
30992                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30993             break;
30994             case "last":
30995                 var total = ds.getTotalCount();
30996                 var extra = total % this.pageSize;
30997                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30998                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30999             break;
31000             case "refresh":
31001                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31002             break;
31003         }
31004     },
31005
31006     /**
31007      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31008      * @param {Roo.data.Store} store The data store to unbind
31009      */
31010     unbind : function(ds){
31011         ds.un("beforeload", this.beforeLoad, this);
31012         ds.un("load", this.onLoad, this);
31013         ds.un("loadexception", this.onLoadError, this);
31014         ds.un("remove", this.updateInfo, this);
31015         ds.un("add", this.updateInfo, this);
31016         this.ds = undefined;
31017     },
31018
31019     /**
31020      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31021      * @param {Roo.data.Store} store The data store to bind
31022      */
31023     bind : function(ds){
31024         ds.on("beforeload", this.beforeLoad, this);
31025         ds.on("load", this.onLoad, this);
31026         ds.on("loadexception", this.onLoadError, this);
31027         ds.on("remove", this.updateInfo, this);
31028         ds.on("add", this.updateInfo, this);
31029         this.ds = ds;
31030     }
31031 });/*
31032  * Based on:
31033  * Ext JS Library 1.1.1
31034  * Copyright(c) 2006-2007, Ext JS, LLC.
31035  *
31036  * Originally Released Under LGPL - original licence link has changed is not relivant.
31037  *
31038  * Fork - LGPL
31039  * <script type="text/javascript">
31040  */
31041
31042 /**
31043  * @class Roo.Resizable
31044  * @extends Roo.util.Observable
31045  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31046  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31047  * 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
31048  * the element will be wrapped for you automatically.</p>
31049  * <p>Here is the list of valid resize handles:</p>
31050  * <pre>
31051 Value   Description
31052 ------  -------------------
31053  'n'     north
31054  's'     south
31055  'e'     east
31056  'w'     west
31057  'nw'    northwest
31058  'sw'    southwest
31059  'se'    southeast
31060  'ne'    northeast
31061  'hd'    horizontal drag
31062  'all'   all
31063 </pre>
31064  * <p>Here's an example showing the creation of a typical Resizable:</p>
31065  * <pre><code>
31066 var resizer = new Roo.Resizable("element-id", {
31067     handles: 'all',
31068     minWidth: 200,
31069     minHeight: 100,
31070     maxWidth: 500,
31071     maxHeight: 400,
31072     pinned: true
31073 });
31074 resizer.on("resize", myHandler);
31075 </code></pre>
31076  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31077  * resizer.east.setDisplayed(false);</p>
31078  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31079  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31080  * resize operation's new size (defaults to [0, 0])
31081  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31082  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31083  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31084  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31085  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31086  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31087  * @cfg {Number} width The width of the element in pixels (defaults to null)
31088  * @cfg {Number} height The height of the element in pixels (defaults to null)
31089  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31090  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31091  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31092  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31093  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31094  * in favor of the handles config option (defaults to false)
31095  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31096  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31097  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31098  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31099  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31100  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
31101  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
31102  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
31103  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
31104  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
31105  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
31106  * @constructor
31107  * Create a new resizable component
31108  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
31109  * @param {Object} config configuration options
31110   */
31111 Roo.Resizable = function(el, config)
31112 {
31113     this.el = Roo.get(el);
31114
31115     if(config && config.wrap){
31116         config.resizeChild = this.el;
31117         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
31118         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
31119         this.el.setStyle("overflow", "hidden");
31120         this.el.setPositioning(config.resizeChild.getPositioning());
31121         config.resizeChild.clearPositioning();
31122         if(!config.width || !config.height){
31123             var csize = config.resizeChild.getSize();
31124             this.el.setSize(csize.width, csize.height);
31125         }
31126         if(config.pinned && !config.adjustments){
31127             config.adjustments = "auto";
31128         }
31129     }
31130
31131     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
31132     this.proxy.unselectable();
31133     this.proxy.enableDisplayMode('block');
31134
31135     Roo.apply(this, config);
31136
31137     if(this.pinned){
31138         this.disableTrackOver = true;
31139         this.el.addClass("x-resizable-pinned");
31140     }
31141     // if the element isn't positioned, make it relative
31142     var position = this.el.getStyle("position");
31143     if(position != "absolute" && position != "fixed"){
31144         this.el.setStyle("position", "relative");
31145     }
31146     if(!this.handles){ // no handles passed, must be legacy style
31147         this.handles = 's,e,se';
31148         if(this.multiDirectional){
31149             this.handles += ',n,w';
31150         }
31151     }
31152     if(this.handles == "all"){
31153         this.handles = "n s e w ne nw se sw";
31154     }
31155     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31156     var ps = Roo.Resizable.positions;
31157     for(var i = 0, len = hs.length; i < len; i++){
31158         if(hs[i] && ps[hs[i]]){
31159             var pos = ps[hs[i]];
31160             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31161         }
31162     }
31163     // legacy
31164     this.corner = this.southeast;
31165     
31166     // updateBox = the box can move..
31167     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31168         this.updateBox = true;
31169     }
31170
31171     this.activeHandle = null;
31172
31173     if(this.resizeChild){
31174         if(typeof this.resizeChild == "boolean"){
31175             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31176         }else{
31177             this.resizeChild = Roo.get(this.resizeChild, true);
31178         }
31179     }
31180     
31181     if(this.adjustments == "auto"){
31182         var rc = this.resizeChild;
31183         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31184         if(rc && (hw || hn)){
31185             rc.position("relative");
31186             rc.setLeft(hw ? hw.el.getWidth() : 0);
31187             rc.setTop(hn ? hn.el.getHeight() : 0);
31188         }
31189         this.adjustments = [
31190             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31191             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31192         ];
31193     }
31194
31195     if(this.draggable){
31196         this.dd = this.dynamic ?
31197             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31198         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31199     }
31200
31201     // public events
31202     this.addEvents({
31203         /**
31204          * @event beforeresize
31205          * Fired before resize is allowed. Set enabled to false to cancel resize.
31206          * @param {Roo.Resizable} this
31207          * @param {Roo.EventObject} e The mousedown event
31208          */
31209         "beforeresize" : true,
31210         /**
31211          * @event resizing
31212          * Fired a resizing.
31213          * @param {Roo.Resizable} this
31214          * @param {Number} x The new x position
31215          * @param {Number} y The new y position
31216          * @param {Number} w The new w width
31217          * @param {Number} h The new h hight
31218          * @param {Roo.EventObject} e The mouseup event
31219          */
31220         "resizing" : true,
31221         /**
31222          * @event resize
31223          * Fired after a resize.
31224          * @param {Roo.Resizable} this
31225          * @param {Number} width The new width
31226          * @param {Number} height The new height
31227          * @param {Roo.EventObject} e The mouseup event
31228          */
31229         "resize" : true
31230     });
31231
31232     if(this.width !== null && this.height !== null){
31233         this.resizeTo(this.width, this.height);
31234     }else{
31235         this.updateChildSize();
31236     }
31237     if(Roo.isIE){
31238         this.el.dom.style.zoom = 1;
31239     }
31240     Roo.Resizable.superclass.constructor.call(this);
31241 };
31242
31243 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31244         resizeChild : false,
31245         adjustments : [0, 0],
31246         minWidth : 5,
31247         minHeight : 5,
31248         maxWidth : 10000,
31249         maxHeight : 10000,
31250         enabled : true,
31251         animate : false,
31252         duration : .35,
31253         dynamic : false,
31254         handles : false,
31255         multiDirectional : false,
31256         disableTrackOver : false,
31257         easing : 'easeOutStrong',
31258         widthIncrement : 0,
31259         heightIncrement : 0,
31260         pinned : false,
31261         width : null,
31262         height : null,
31263         preserveRatio : false,
31264         transparent: false,
31265         minX: 0,
31266         minY: 0,
31267         draggable: false,
31268
31269         /**
31270          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31271          */
31272         constrainTo: undefined,
31273         /**
31274          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31275          */
31276         resizeRegion: undefined,
31277
31278
31279     /**
31280      * Perform a manual resize
31281      * @param {Number} width
31282      * @param {Number} height
31283      */
31284     resizeTo : function(width, height){
31285         this.el.setSize(width, height);
31286         this.updateChildSize();
31287         this.fireEvent("resize", this, width, height, null);
31288     },
31289
31290     // private
31291     startSizing : function(e, handle){
31292         this.fireEvent("beforeresize", this, e);
31293         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31294
31295             if(!this.overlay){
31296                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31297                 this.overlay.unselectable();
31298                 this.overlay.enableDisplayMode("block");
31299                 this.overlay.on("mousemove", this.onMouseMove, this);
31300                 this.overlay.on("mouseup", this.onMouseUp, this);
31301             }
31302             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31303
31304             this.resizing = true;
31305             this.startBox = this.el.getBox();
31306             this.startPoint = e.getXY();
31307             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31308                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31309
31310             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31311             this.overlay.show();
31312
31313             if(this.constrainTo) {
31314                 var ct = Roo.get(this.constrainTo);
31315                 this.resizeRegion = ct.getRegion().adjust(
31316                     ct.getFrameWidth('t'),
31317                     ct.getFrameWidth('l'),
31318                     -ct.getFrameWidth('b'),
31319                     -ct.getFrameWidth('r')
31320                 );
31321             }
31322
31323             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31324             this.proxy.show();
31325             this.proxy.setBox(this.startBox);
31326             if(!this.dynamic){
31327                 this.proxy.setStyle('visibility', 'visible');
31328             }
31329         }
31330     },
31331
31332     // private
31333     onMouseDown : function(handle, e){
31334         if(this.enabled){
31335             e.stopEvent();
31336             this.activeHandle = handle;
31337             this.startSizing(e, handle);
31338         }
31339     },
31340
31341     // private
31342     onMouseUp : function(e){
31343         var size = this.resizeElement();
31344         this.resizing = false;
31345         this.handleOut();
31346         this.overlay.hide();
31347         this.proxy.hide();
31348         this.fireEvent("resize", this, size.width, size.height, e);
31349     },
31350
31351     // private
31352     updateChildSize : function(){
31353         
31354         if(this.resizeChild){
31355             var el = this.el;
31356             var child = this.resizeChild;
31357             var adj = this.adjustments;
31358             if(el.dom.offsetWidth){
31359                 var b = el.getSize(true);
31360                 child.setSize(b.width+adj[0], b.height+adj[1]);
31361             }
31362             // Second call here for IE
31363             // The first call enables instant resizing and
31364             // the second call corrects scroll bars if they
31365             // exist
31366             if(Roo.isIE){
31367                 setTimeout(function(){
31368                     if(el.dom.offsetWidth){
31369                         var b = el.getSize(true);
31370                         child.setSize(b.width+adj[0], b.height+adj[1]);
31371                     }
31372                 }, 10);
31373             }
31374         }
31375     },
31376
31377     // private
31378     snap : function(value, inc, min){
31379         if(!inc || !value) {
31380             return value;
31381         }
31382         var newValue = value;
31383         var m = value % inc;
31384         if(m > 0){
31385             if(m > (inc/2)){
31386                 newValue = value + (inc-m);
31387             }else{
31388                 newValue = value - m;
31389             }
31390         }
31391         return Math.max(min, newValue);
31392     },
31393
31394     // private
31395     resizeElement : function(){
31396         var box = this.proxy.getBox();
31397         if(this.updateBox){
31398             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31399         }else{
31400             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31401         }
31402         this.updateChildSize();
31403         if(!this.dynamic){
31404             this.proxy.hide();
31405         }
31406         return box;
31407     },
31408
31409     // private
31410     constrain : function(v, diff, m, mx){
31411         if(v - diff < m){
31412             diff = v - m;
31413         }else if(v - diff > mx){
31414             diff = mx - v;
31415         }
31416         return diff;
31417     },
31418
31419     // private
31420     onMouseMove : function(e){
31421         
31422         if(this.enabled){
31423             try{// try catch so if something goes wrong the user doesn't get hung
31424
31425             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31426                 return;
31427             }
31428
31429             //var curXY = this.startPoint;
31430             var curSize = this.curSize || this.startBox;
31431             var x = this.startBox.x, y = this.startBox.y;
31432             var ox = x, oy = y;
31433             var w = curSize.width, h = curSize.height;
31434             var ow = w, oh = h;
31435             var mw = this.minWidth, mh = this.minHeight;
31436             var mxw = this.maxWidth, mxh = this.maxHeight;
31437             var wi = this.widthIncrement;
31438             var hi = this.heightIncrement;
31439
31440             var eventXY = e.getXY();
31441             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31442             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31443
31444             var pos = this.activeHandle.position;
31445
31446             switch(pos){
31447                 case "east":
31448                     w += diffX;
31449                     w = Math.min(Math.max(mw, w), mxw);
31450                     break;
31451              
31452                 case "south":
31453                     h += diffY;
31454                     h = Math.min(Math.max(mh, h), mxh);
31455                     break;
31456                 case "southeast":
31457                     w += diffX;
31458                     h += diffY;
31459                     w = Math.min(Math.max(mw, w), mxw);
31460                     h = Math.min(Math.max(mh, h), mxh);
31461                     break;
31462                 case "north":
31463                     diffY = this.constrain(h, diffY, mh, mxh);
31464                     y += diffY;
31465                     h -= diffY;
31466                     break;
31467                 case "hdrag":
31468                     
31469                     if (wi) {
31470                         var adiffX = Math.abs(diffX);
31471                         var sub = (adiffX % wi); // how much 
31472                         if (sub > (wi/2)) { // far enough to snap
31473                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31474                         } else {
31475                             // remove difference.. 
31476                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31477                         }
31478                     }
31479                     x += diffX;
31480                     x = Math.max(this.minX, x);
31481                     break;
31482                 case "west":
31483                     diffX = this.constrain(w, diffX, mw, mxw);
31484                     x += diffX;
31485                     w -= diffX;
31486                     break;
31487                 case "northeast":
31488                     w += diffX;
31489                     w = Math.min(Math.max(mw, w), mxw);
31490                     diffY = this.constrain(h, diffY, mh, mxh);
31491                     y += diffY;
31492                     h -= diffY;
31493                     break;
31494                 case "northwest":
31495                     diffX = this.constrain(w, diffX, mw, mxw);
31496                     diffY = this.constrain(h, diffY, mh, mxh);
31497                     y += diffY;
31498                     h -= diffY;
31499                     x += diffX;
31500                     w -= diffX;
31501                     break;
31502                case "southwest":
31503                     diffX = this.constrain(w, diffX, mw, mxw);
31504                     h += diffY;
31505                     h = Math.min(Math.max(mh, h), mxh);
31506                     x += diffX;
31507                     w -= diffX;
31508                     break;
31509             }
31510
31511             var sw = this.snap(w, wi, mw);
31512             var sh = this.snap(h, hi, mh);
31513             if(sw != w || sh != h){
31514                 switch(pos){
31515                     case "northeast":
31516                         y -= sh - h;
31517                     break;
31518                     case "north":
31519                         y -= sh - h;
31520                         break;
31521                     case "southwest":
31522                         x -= sw - w;
31523                     break;
31524                     case "west":
31525                         x -= sw - w;
31526                         break;
31527                     case "northwest":
31528                         x -= sw - w;
31529                         y -= sh - h;
31530                     break;
31531                 }
31532                 w = sw;
31533                 h = sh;
31534             }
31535
31536             if(this.preserveRatio){
31537                 switch(pos){
31538                     case "southeast":
31539                     case "east":
31540                         h = oh * (w/ow);
31541                         h = Math.min(Math.max(mh, h), mxh);
31542                         w = ow * (h/oh);
31543                        break;
31544                     case "south":
31545                         w = ow * (h/oh);
31546                         w = Math.min(Math.max(mw, w), mxw);
31547                         h = oh * (w/ow);
31548                         break;
31549                     case "northeast":
31550                         w = ow * (h/oh);
31551                         w = Math.min(Math.max(mw, w), mxw);
31552                         h = oh * (w/ow);
31553                     break;
31554                     case "north":
31555                         var tw = w;
31556                         w = ow * (h/oh);
31557                         w = Math.min(Math.max(mw, w), mxw);
31558                         h = oh * (w/ow);
31559                         x += (tw - w) / 2;
31560                         break;
31561                     case "southwest":
31562                         h = oh * (w/ow);
31563                         h = Math.min(Math.max(mh, h), mxh);
31564                         var tw = w;
31565                         w = ow * (h/oh);
31566                         x += tw - w;
31567                         break;
31568                     case "west":
31569                         var th = h;
31570                         h = oh * (w/ow);
31571                         h = Math.min(Math.max(mh, h), mxh);
31572                         y += (th - h) / 2;
31573                         var tw = w;
31574                         w = ow * (h/oh);
31575                         x += tw - w;
31576                        break;
31577                     case "northwest":
31578                         var tw = w;
31579                         var th = h;
31580                         h = oh * (w/ow);
31581                         h = Math.min(Math.max(mh, h), mxh);
31582                         w = ow * (h/oh);
31583                         y += th - h;
31584                         x += tw - w;
31585                        break;
31586
31587                 }
31588             }
31589             if (pos == 'hdrag') {
31590                 w = ow;
31591             }
31592             this.proxy.setBounds(x, y, w, h);
31593             if(this.dynamic){
31594                 this.resizeElement();
31595             }
31596             }catch(e){}
31597         }
31598         this.fireEvent("resizing", this, x, y, w, h, e);
31599     },
31600
31601     // private
31602     handleOver : function(){
31603         if(this.enabled){
31604             this.el.addClass("x-resizable-over");
31605         }
31606     },
31607
31608     // private
31609     handleOut : function(){
31610         if(!this.resizing){
31611             this.el.removeClass("x-resizable-over");
31612         }
31613     },
31614
31615     /**
31616      * Returns the element this component is bound to.
31617      * @return {Roo.Element}
31618      */
31619     getEl : function(){
31620         return this.el;
31621     },
31622
31623     /**
31624      * Returns the resizeChild element (or null).
31625      * @return {Roo.Element}
31626      */
31627     getResizeChild : function(){
31628         return this.resizeChild;
31629     },
31630     groupHandler : function()
31631     {
31632         
31633     },
31634     /**
31635      * Destroys this resizable. If the element was wrapped and
31636      * removeEl is not true then the element remains.
31637      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31638      */
31639     destroy : function(removeEl){
31640         this.proxy.remove();
31641         if(this.overlay){
31642             this.overlay.removeAllListeners();
31643             this.overlay.remove();
31644         }
31645         var ps = Roo.Resizable.positions;
31646         for(var k in ps){
31647             if(typeof ps[k] != "function" && this[ps[k]]){
31648                 var h = this[ps[k]];
31649                 h.el.removeAllListeners();
31650                 h.el.remove();
31651             }
31652         }
31653         if(removeEl){
31654             this.el.update("");
31655             this.el.remove();
31656         }
31657     }
31658 });
31659
31660 // private
31661 // hash to map config positions to true positions
31662 Roo.Resizable.positions = {
31663     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31664     hd: "hdrag"
31665 };
31666
31667 // private
31668 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31669     if(!this.tpl){
31670         // only initialize the template if resizable is used
31671         var tpl = Roo.DomHelper.createTemplate(
31672             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31673         );
31674         tpl.compile();
31675         Roo.Resizable.Handle.prototype.tpl = tpl;
31676     }
31677     this.position = pos;
31678     this.rz = rz;
31679     // show north drag fro topdra
31680     var handlepos = pos == 'hdrag' ? 'north' : pos;
31681     
31682     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31683     if (pos == 'hdrag') {
31684         this.el.setStyle('cursor', 'pointer');
31685     }
31686     this.el.unselectable();
31687     if(transparent){
31688         this.el.setOpacity(0);
31689     }
31690     this.el.on("mousedown", this.onMouseDown, this);
31691     if(!disableTrackOver){
31692         this.el.on("mouseover", this.onMouseOver, this);
31693         this.el.on("mouseout", this.onMouseOut, this);
31694     }
31695 };
31696
31697 // private
31698 Roo.Resizable.Handle.prototype = {
31699     afterResize : function(rz){
31700         Roo.log('after?');
31701         // do nothing
31702     },
31703     // private
31704     onMouseDown : function(e){
31705         this.rz.onMouseDown(this, e);
31706     },
31707     // private
31708     onMouseOver : function(e){
31709         this.rz.handleOver(this, e);
31710     },
31711     // private
31712     onMouseOut : function(e){
31713         this.rz.handleOut(this, e);
31714     }
31715 };/*
31716  * Based on:
31717  * Ext JS Library 1.1.1
31718  * Copyright(c) 2006-2007, Ext JS, LLC.
31719  *
31720  * Originally Released Under LGPL - original licence link has changed is not relivant.
31721  *
31722  * Fork - LGPL
31723  * <script type="text/javascript">
31724  */
31725
31726 /**
31727  * @class Roo.Editor
31728  * @extends Roo.Component
31729  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31730  * @constructor
31731  * Create a new Editor
31732  * @param {Roo.form.Field} field The Field object (or descendant)
31733  * @param {Object} config The config object
31734  */
31735 Roo.Editor = function(field, config){
31736     Roo.Editor.superclass.constructor.call(this, config);
31737     this.field = field;
31738     this.addEvents({
31739         /**
31740              * @event beforestartedit
31741              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31742              * false from the handler of this event.
31743              * @param {Editor} this
31744              * @param {Roo.Element} boundEl The underlying element bound to this editor
31745              * @param {Mixed} value The field value being set
31746              */
31747         "beforestartedit" : true,
31748         /**
31749              * @event startedit
31750              * Fires when this editor is displayed
31751              * @param {Roo.Element} boundEl The underlying element bound to this editor
31752              * @param {Mixed} value The starting field value
31753              */
31754         "startedit" : true,
31755         /**
31756              * @event beforecomplete
31757              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31758              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31759              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31760              * event will not fire since no edit actually occurred.
31761              * @param {Editor} this
31762              * @param {Mixed} value The current field value
31763              * @param {Mixed} startValue The original field value
31764              */
31765         "beforecomplete" : true,
31766         /**
31767              * @event complete
31768              * Fires after editing is complete and any changed value has been written to the underlying field.
31769              * @param {Editor} this
31770              * @param {Mixed} value The current field value
31771              * @param {Mixed} startValue The original field value
31772              */
31773         "complete" : true,
31774         /**
31775          * @event specialkey
31776          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31777          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31778          * @param {Roo.form.Field} this
31779          * @param {Roo.EventObject} e The event object
31780          */
31781         "specialkey" : true
31782     });
31783 };
31784
31785 Roo.extend(Roo.Editor, Roo.Component, {
31786     /**
31787      * @cfg {Boolean/String} autosize
31788      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31789      * or "height" to adopt the height only (defaults to false)
31790      */
31791     /**
31792      * @cfg {Boolean} revertInvalid
31793      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31794      * validation fails (defaults to true)
31795      */
31796     /**
31797      * @cfg {Boolean} ignoreNoChange
31798      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31799      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31800      * will never be ignored.
31801      */
31802     /**
31803      * @cfg {Boolean} hideEl
31804      * False to keep the bound element visible while the editor is displayed (defaults to true)
31805      */
31806     /**
31807      * @cfg {Mixed} value
31808      * The data value of the underlying field (defaults to "")
31809      */
31810     value : "",
31811     /**
31812      * @cfg {String} alignment
31813      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31814      */
31815     alignment: "c-c?",
31816     /**
31817      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31818      * for bottom-right shadow (defaults to "frame")
31819      */
31820     shadow : "frame",
31821     /**
31822      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31823      */
31824     constrain : false,
31825     /**
31826      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31827      */
31828     completeOnEnter : false,
31829     /**
31830      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31831      */
31832     cancelOnEsc : false,
31833     /**
31834      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31835      */
31836     updateEl : false,
31837
31838     // private
31839     onRender : function(ct, position){
31840         this.el = new Roo.Layer({
31841             shadow: this.shadow,
31842             cls: "x-editor",
31843             parentEl : ct,
31844             shim : this.shim,
31845             shadowOffset:4,
31846             id: this.id,
31847             constrain: this.constrain
31848         });
31849         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31850         if(this.field.msgTarget != 'title'){
31851             this.field.msgTarget = 'qtip';
31852         }
31853         this.field.render(this.el);
31854         if(Roo.isGecko){
31855             this.field.el.dom.setAttribute('autocomplete', 'off');
31856         }
31857         this.field.on("specialkey", this.onSpecialKey, this);
31858         if(this.swallowKeys){
31859             this.field.el.swallowEvent(['keydown','keypress']);
31860         }
31861         this.field.show();
31862         this.field.on("blur", this.onBlur, this);
31863         if(this.field.grow){
31864             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31865         }
31866     },
31867
31868     onSpecialKey : function(field, e)
31869     {
31870         //Roo.log('editor onSpecialKey');
31871         if(this.completeOnEnter && e.getKey() == e.ENTER){
31872             e.stopEvent();
31873             this.completeEdit();
31874             return;
31875         }
31876         // do not fire special key otherwise it might hide close the editor...
31877         if(e.getKey() == e.ENTER){    
31878             return;
31879         }
31880         if(this.cancelOnEsc && e.getKey() == e.ESC){
31881             this.cancelEdit();
31882             return;
31883         } 
31884         this.fireEvent('specialkey', field, e);
31885     
31886     },
31887
31888     /**
31889      * Starts the editing process and shows the editor.
31890      * @param {String/HTMLElement/Element} el The element to edit
31891      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31892       * to the innerHTML of el.
31893      */
31894     startEdit : function(el, value){
31895         if(this.editing){
31896             this.completeEdit();
31897         }
31898         this.boundEl = Roo.get(el);
31899         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31900         if(!this.rendered){
31901             this.render(this.parentEl || document.body);
31902         }
31903         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31904             return;
31905         }
31906         this.startValue = v;
31907         this.field.setValue(v);
31908         if(this.autoSize){
31909             var sz = this.boundEl.getSize();
31910             switch(this.autoSize){
31911                 case "width":
31912                 this.setSize(sz.width,  "");
31913                 break;
31914                 case "height":
31915                 this.setSize("",  sz.height);
31916                 break;
31917                 default:
31918                 this.setSize(sz.width,  sz.height);
31919             }
31920         }
31921         this.el.alignTo(this.boundEl, this.alignment);
31922         this.editing = true;
31923         if(Roo.QuickTips){
31924             Roo.QuickTips.disable();
31925         }
31926         this.show();
31927     },
31928
31929     /**
31930      * Sets the height and width of this editor.
31931      * @param {Number} width The new width
31932      * @param {Number} height The new height
31933      */
31934     setSize : function(w, h){
31935         this.field.setSize(w, h);
31936         if(this.el){
31937             this.el.sync();
31938         }
31939     },
31940
31941     /**
31942      * Realigns the editor to the bound field based on the current alignment config value.
31943      */
31944     realign : function(){
31945         this.el.alignTo(this.boundEl, this.alignment);
31946     },
31947
31948     /**
31949      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31950      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31951      */
31952     completeEdit : function(remainVisible){
31953         if(!this.editing){
31954             return;
31955         }
31956         var v = this.getValue();
31957         if(this.revertInvalid !== false && !this.field.isValid()){
31958             v = this.startValue;
31959             this.cancelEdit(true);
31960         }
31961         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31962             this.editing = false;
31963             this.hide();
31964             return;
31965         }
31966         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31967             this.editing = false;
31968             if(this.updateEl && this.boundEl){
31969                 this.boundEl.update(v);
31970             }
31971             if(remainVisible !== true){
31972                 this.hide();
31973             }
31974             this.fireEvent("complete", this, v, this.startValue);
31975         }
31976     },
31977
31978     // private
31979     onShow : function(){
31980         this.el.show();
31981         if(this.hideEl !== false){
31982             this.boundEl.hide();
31983         }
31984         this.field.show();
31985         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31986             this.fixIEFocus = true;
31987             this.deferredFocus.defer(50, this);
31988         }else{
31989             this.field.focus();
31990         }
31991         this.fireEvent("startedit", this.boundEl, this.startValue);
31992     },
31993
31994     deferredFocus : function(){
31995         if(this.editing){
31996             this.field.focus();
31997         }
31998     },
31999
32000     /**
32001      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32002      * reverted to the original starting value.
32003      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32004      * cancel (defaults to false)
32005      */
32006     cancelEdit : function(remainVisible){
32007         if(this.editing){
32008             this.setValue(this.startValue);
32009             if(remainVisible !== true){
32010                 this.hide();
32011             }
32012         }
32013     },
32014
32015     // private
32016     onBlur : function(){
32017         if(this.allowBlur !== true && this.editing){
32018             this.completeEdit();
32019         }
32020     },
32021
32022     // private
32023     onHide : function(){
32024         if(this.editing){
32025             this.completeEdit();
32026             return;
32027         }
32028         this.field.blur();
32029         if(this.field.collapse){
32030             this.field.collapse();
32031         }
32032         this.el.hide();
32033         if(this.hideEl !== false){
32034             this.boundEl.show();
32035         }
32036         if(Roo.QuickTips){
32037             Roo.QuickTips.enable();
32038         }
32039     },
32040
32041     /**
32042      * Sets the data value of the editor
32043      * @param {Mixed} value Any valid value supported by the underlying field
32044      */
32045     setValue : function(v){
32046         this.field.setValue(v);
32047     },
32048
32049     /**
32050      * Gets the data value of the editor
32051      * @return {Mixed} The data value
32052      */
32053     getValue : function(){
32054         return this.field.getValue();
32055     }
32056 });/*
32057  * Based on:
32058  * Ext JS Library 1.1.1
32059  * Copyright(c) 2006-2007, Ext JS, LLC.
32060  *
32061  * Originally Released Under LGPL - original licence link has changed is not relivant.
32062  *
32063  * Fork - LGPL
32064  * <script type="text/javascript">
32065  */
32066  
32067 /**
32068  * @class Roo.BasicDialog
32069  * @extends Roo.util.Observable
32070  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32071  * <pre><code>
32072 var dlg = new Roo.BasicDialog("my-dlg", {
32073     height: 200,
32074     width: 300,
32075     minHeight: 100,
32076     minWidth: 150,
32077     modal: true,
32078     proxyDrag: true,
32079     shadow: true
32080 });
32081 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32082 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32083 dlg.addButton('Cancel', dlg.hide, dlg);
32084 dlg.show();
32085 </code></pre>
32086   <b>A Dialog should always be a direct child of the body element.</b>
32087  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32088  * @cfg {String} title Default text to display in the title bar (defaults to null)
32089  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32090  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32091  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32092  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32093  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32094  * (defaults to null with no animation)
32095  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32096  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32097  * property for valid values (defaults to 'all')
32098  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32099  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32100  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
32101  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
32102  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
32103  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
32104  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
32105  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
32106  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
32107  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
32108  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
32109  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
32110  * draggable = true (defaults to false)
32111  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
32112  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32113  * shadow (defaults to false)
32114  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
32115  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
32116  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
32117  * @cfg {Array} buttons Array of buttons
32118  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
32119  * @constructor
32120  * Create a new BasicDialog.
32121  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
32122  * @param {Object} config Configuration options
32123  */
32124 Roo.BasicDialog = function(el, config){
32125     this.el = Roo.get(el);
32126     var dh = Roo.DomHelper;
32127     if(!this.el && config && config.autoCreate){
32128         if(typeof config.autoCreate == "object"){
32129             if(!config.autoCreate.id){
32130                 config.autoCreate.id = el;
32131             }
32132             this.el = dh.append(document.body,
32133                         config.autoCreate, true);
32134         }else{
32135             this.el = dh.append(document.body,
32136                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32137         }
32138     }
32139     el = this.el;
32140     el.setDisplayed(true);
32141     el.hide = this.hideAction;
32142     this.id = el.id;
32143     el.addClass("x-dlg");
32144
32145     Roo.apply(this, config);
32146
32147     this.proxy = el.createProxy("x-dlg-proxy");
32148     this.proxy.hide = this.hideAction;
32149     this.proxy.setOpacity(.5);
32150     this.proxy.hide();
32151
32152     if(config.width){
32153         el.setWidth(config.width);
32154     }
32155     if(config.height){
32156         el.setHeight(config.height);
32157     }
32158     this.size = el.getSize();
32159     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32160         this.xy = [config.x,config.y];
32161     }else{
32162         this.xy = el.getCenterXY(true);
32163     }
32164     /** The header element @type Roo.Element */
32165     this.header = el.child("> .x-dlg-hd");
32166     /** The body element @type Roo.Element */
32167     this.body = el.child("> .x-dlg-bd");
32168     /** The footer element @type Roo.Element */
32169     this.footer = el.child("> .x-dlg-ft");
32170
32171     if(!this.header){
32172         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32173     }
32174     if(!this.body){
32175         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32176     }
32177
32178     this.header.unselectable();
32179     if(this.title){
32180         this.header.update(this.title);
32181     }
32182     // this element allows the dialog to be focused for keyboard event
32183     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32184     this.focusEl.swallowEvent("click", true);
32185
32186     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32187
32188     // wrap the body and footer for special rendering
32189     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32190     if(this.footer){
32191         this.bwrap.dom.appendChild(this.footer.dom);
32192     }
32193
32194     this.bg = this.el.createChild({
32195         tag: "div", cls:"x-dlg-bg",
32196         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32197     });
32198     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32199
32200
32201     if(this.autoScroll !== false && !this.autoTabs){
32202         this.body.setStyle("overflow", "auto");
32203     }
32204
32205     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32206
32207     if(this.closable !== false){
32208         this.el.addClass("x-dlg-closable");
32209         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32210         this.close.on("click", this.closeClick, this);
32211         this.close.addClassOnOver("x-dlg-close-over");
32212     }
32213     if(this.collapsible !== false){
32214         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32215         this.collapseBtn.on("click", this.collapseClick, this);
32216         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32217         this.header.on("dblclick", this.collapseClick, this);
32218     }
32219     if(this.resizable !== false){
32220         this.el.addClass("x-dlg-resizable");
32221         this.resizer = new Roo.Resizable(el, {
32222             minWidth: this.minWidth || 80,
32223             minHeight:this.minHeight || 80,
32224             handles: this.resizeHandles || "all",
32225             pinned: true
32226         });
32227         this.resizer.on("beforeresize", this.beforeResize, this);
32228         this.resizer.on("resize", this.onResize, this);
32229     }
32230     if(this.draggable !== false){
32231         el.addClass("x-dlg-draggable");
32232         if (!this.proxyDrag) {
32233             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32234         }
32235         else {
32236             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32237         }
32238         dd.setHandleElId(this.header.id);
32239         dd.endDrag = this.endMove.createDelegate(this);
32240         dd.startDrag = this.startMove.createDelegate(this);
32241         dd.onDrag = this.onDrag.createDelegate(this);
32242         dd.scroll = false;
32243         this.dd = dd;
32244     }
32245     if(this.modal){
32246         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32247         this.mask.enableDisplayMode("block");
32248         this.mask.hide();
32249         this.el.addClass("x-dlg-modal");
32250     }
32251     if(this.shadow){
32252         this.shadow = new Roo.Shadow({
32253             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32254             offset : this.shadowOffset
32255         });
32256     }else{
32257         this.shadowOffset = 0;
32258     }
32259     if(Roo.useShims && this.shim !== false){
32260         this.shim = this.el.createShim();
32261         this.shim.hide = this.hideAction;
32262         this.shim.hide();
32263     }else{
32264         this.shim = false;
32265     }
32266     if(this.autoTabs){
32267         this.initTabs();
32268     }
32269     if (this.buttons) { 
32270         var bts= this.buttons;
32271         this.buttons = [];
32272         Roo.each(bts, function(b) {
32273             this.addButton(b);
32274         }, this);
32275     }
32276     
32277     
32278     this.addEvents({
32279         /**
32280          * @event keydown
32281          * Fires when a key is pressed
32282          * @param {Roo.BasicDialog} this
32283          * @param {Roo.EventObject} e
32284          */
32285         "keydown" : true,
32286         /**
32287          * @event move
32288          * Fires when this dialog is moved by the user.
32289          * @param {Roo.BasicDialog} this
32290          * @param {Number} x The new page X
32291          * @param {Number} y The new page Y
32292          */
32293         "move" : true,
32294         /**
32295          * @event resize
32296          * Fires when this dialog is resized by the user.
32297          * @param {Roo.BasicDialog} this
32298          * @param {Number} width The new width
32299          * @param {Number} height The new height
32300          */
32301         "resize" : true,
32302         /**
32303          * @event beforehide
32304          * Fires before this dialog is hidden.
32305          * @param {Roo.BasicDialog} this
32306          */
32307         "beforehide" : true,
32308         /**
32309          * @event hide
32310          * Fires when this dialog is hidden.
32311          * @param {Roo.BasicDialog} this
32312          */
32313         "hide" : true,
32314         /**
32315          * @event beforeshow
32316          * Fires before this dialog is shown.
32317          * @param {Roo.BasicDialog} this
32318          */
32319         "beforeshow" : true,
32320         /**
32321          * @event show
32322          * Fires when this dialog is shown.
32323          * @param {Roo.BasicDialog} this
32324          */
32325         "show" : true
32326     });
32327     el.on("keydown", this.onKeyDown, this);
32328     el.on("mousedown", this.toFront, this);
32329     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32330     this.el.hide();
32331     Roo.DialogManager.register(this);
32332     Roo.BasicDialog.superclass.constructor.call(this);
32333 };
32334
32335 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32336     shadowOffset: Roo.isIE ? 6 : 5,
32337     minHeight: 80,
32338     minWidth: 200,
32339     minButtonWidth: 75,
32340     defaultButton: null,
32341     buttonAlign: "right",
32342     tabTag: 'div',
32343     firstShow: true,
32344
32345     /**
32346      * Sets the dialog title text
32347      * @param {String} text The title text to display
32348      * @return {Roo.BasicDialog} this
32349      */
32350     setTitle : function(text){
32351         this.header.update(text);
32352         return this;
32353     },
32354
32355     // private
32356     closeClick : function(){
32357         this.hide();
32358     },
32359
32360     // private
32361     collapseClick : function(){
32362         this[this.collapsed ? "expand" : "collapse"]();
32363     },
32364
32365     /**
32366      * Collapses the dialog to its minimized state (only the title bar is visible).
32367      * Equivalent to the user clicking the collapse dialog button.
32368      */
32369     collapse : function(){
32370         if(!this.collapsed){
32371             this.collapsed = true;
32372             this.el.addClass("x-dlg-collapsed");
32373             this.restoreHeight = this.el.getHeight();
32374             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32375         }
32376     },
32377
32378     /**
32379      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32380      * clicking the expand dialog button.
32381      */
32382     expand : function(){
32383         if(this.collapsed){
32384             this.collapsed = false;
32385             this.el.removeClass("x-dlg-collapsed");
32386             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32387         }
32388     },
32389
32390     /**
32391      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32392      * @return {Roo.TabPanel} The tabs component
32393      */
32394     initTabs : function(){
32395         var tabs = this.getTabs();
32396         while(tabs.getTab(0)){
32397             tabs.removeTab(0);
32398         }
32399         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32400             var dom = el.dom;
32401             tabs.addTab(Roo.id(dom), dom.title);
32402             dom.title = "";
32403         });
32404         tabs.activate(0);
32405         return tabs;
32406     },
32407
32408     // private
32409     beforeResize : function(){
32410         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32411     },
32412
32413     // private
32414     onResize : function(){
32415         this.refreshSize();
32416         this.syncBodyHeight();
32417         this.adjustAssets();
32418         this.focus();
32419         this.fireEvent("resize", this, this.size.width, this.size.height);
32420     },
32421
32422     // private
32423     onKeyDown : function(e){
32424         if(this.isVisible()){
32425             this.fireEvent("keydown", this, e);
32426         }
32427     },
32428
32429     /**
32430      * Resizes the dialog.
32431      * @param {Number} width
32432      * @param {Number} height
32433      * @return {Roo.BasicDialog} this
32434      */
32435     resizeTo : function(width, height){
32436         this.el.setSize(width, height);
32437         this.size = {width: width, height: height};
32438         this.syncBodyHeight();
32439         if(this.fixedcenter){
32440             this.center();
32441         }
32442         if(this.isVisible()){
32443             this.constrainXY();
32444             this.adjustAssets();
32445         }
32446         this.fireEvent("resize", this, width, height);
32447         return this;
32448     },
32449
32450
32451     /**
32452      * Resizes the dialog to fit the specified content size.
32453      * @param {Number} width
32454      * @param {Number} height
32455      * @return {Roo.BasicDialog} this
32456      */
32457     setContentSize : function(w, h){
32458         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32459         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32460         //if(!this.el.isBorderBox()){
32461             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32462             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32463         //}
32464         if(this.tabs){
32465             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32466             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32467         }
32468         this.resizeTo(w, h);
32469         return this;
32470     },
32471
32472     /**
32473      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32474      * executed in response to a particular key being pressed while the dialog is active.
32475      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32476      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32477      * @param {Function} fn The function to call
32478      * @param {Object} scope (optional) The scope of the function
32479      * @return {Roo.BasicDialog} this
32480      */
32481     addKeyListener : function(key, fn, scope){
32482         var keyCode, shift, ctrl, alt;
32483         if(typeof key == "object" && !(key instanceof Array)){
32484             keyCode = key["key"];
32485             shift = key["shift"];
32486             ctrl = key["ctrl"];
32487             alt = key["alt"];
32488         }else{
32489             keyCode = key;
32490         }
32491         var handler = function(dlg, e){
32492             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32493                 var k = e.getKey();
32494                 if(keyCode instanceof Array){
32495                     for(var i = 0, len = keyCode.length; i < len; i++){
32496                         if(keyCode[i] == k){
32497                           fn.call(scope || window, dlg, k, e);
32498                           return;
32499                         }
32500                     }
32501                 }else{
32502                     if(k == keyCode){
32503                         fn.call(scope || window, dlg, k, e);
32504                     }
32505                 }
32506             }
32507         };
32508         this.on("keydown", handler);
32509         return this;
32510     },
32511
32512     /**
32513      * Returns the TabPanel component (creates it if it doesn't exist).
32514      * Note: If you wish to simply check for the existence of tabs without creating them,
32515      * check for a null 'tabs' property.
32516      * @return {Roo.TabPanel} The tabs component
32517      */
32518     getTabs : function(){
32519         if(!this.tabs){
32520             this.el.addClass("x-dlg-auto-tabs");
32521             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32522             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32523         }
32524         return this.tabs;
32525     },
32526
32527     /**
32528      * Adds a button to the footer section of the dialog.
32529      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32530      * object or a valid Roo.DomHelper element config
32531      * @param {Function} handler The function called when the button is clicked
32532      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32533      * @return {Roo.Button} The new button
32534      */
32535     addButton : function(config, handler, scope){
32536         var dh = Roo.DomHelper;
32537         if(!this.footer){
32538             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32539         }
32540         if(!this.btnContainer){
32541             var tb = this.footer.createChild({
32542
32543                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32544                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32545             }, null, true);
32546             this.btnContainer = tb.firstChild.firstChild.firstChild;
32547         }
32548         var bconfig = {
32549             handler: handler,
32550             scope: scope,
32551             minWidth: this.minButtonWidth,
32552             hideParent:true
32553         };
32554         if(typeof config == "string"){
32555             bconfig.text = config;
32556         }else{
32557             if(config.tag){
32558                 bconfig.dhconfig = config;
32559             }else{
32560                 Roo.apply(bconfig, config);
32561             }
32562         }
32563         var fc = false;
32564         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32565             bconfig.position = Math.max(0, bconfig.position);
32566             fc = this.btnContainer.childNodes[bconfig.position];
32567         }
32568          
32569         var btn = new Roo.Button(
32570             fc ? 
32571                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32572                 : this.btnContainer.appendChild(document.createElement("td")),
32573             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32574             bconfig
32575         );
32576         this.syncBodyHeight();
32577         if(!this.buttons){
32578             /**
32579              * Array of all the buttons that have been added to this dialog via addButton
32580              * @type Array
32581              */
32582             this.buttons = [];
32583         }
32584         this.buttons.push(btn);
32585         return btn;
32586     },
32587
32588     /**
32589      * Sets the default button to be focused when the dialog is displayed.
32590      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32591      * @return {Roo.BasicDialog} this
32592      */
32593     setDefaultButton : function(btn){
32594         this.defaultButton = btn;
32595         return this;
32596     },
32597
32598     // private
32599     getHeaderFooterHeight : function(safe){
32600         var height = 0;
32601         if(this.header){
32602            height += this.header.getHeight();
32603         }
32604         if(this.footer){
32605            var fm = this.footer.getMargins();
32606             height += (this.footer.getHeight()+fm.top+fm.bottom);
32607         }
32608         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32609         height += this.centerBg.getPadding("tb");
32610         return height;
32611     },
32612
32613     // private
32614     syncBodyHeight : function()
32615     {
32616         var bd = this.body, // the text
32617             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32618             bw = this.bwrap;
32619         var height = this.size.height - this.getHeaderFooterHeight(false);
32620         bd.setHeight(height-bd.getMargins("tb"));
32621         var hh = this.header.getHeight();
32622         var h = this.size.height-hh;
32623         cb.setHeight(h);
32624         
32625         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32626         bw.setHeight(h-cb.getPadding("tb"));
32627         
32628         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32629         bd.setWidth(bw.getWidth(true));
32630         if(this.tabs){
32631             this.tabs.syncHeight();
32632             if(Roo.isIE){
32633                 this.tabs.el.repaint();
32634             }
32635         }
32636     },
32637
32638     /**
32639      * Restores the previous state of the dialog if Roo.state is configured.
32640      * @return {Roo.BasicDialog} this
32641      */
32642     restoreState : function(){
32643         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32644         if(box && box.width){
32645             this.xy = [box.x, box.y];
32646             this.resizeTo(box.width, box.height);
32647         }
32648         return this;
32649     },
32650
32651     // private
32652     beforeShow : function(){
32653         this.expand();
32654         if(this.fixedcenter){
32655             this.xy = this.el.getCenterXY(true);
32656         }
32657         if(this.modal){
32658             Roo.get(document.body).addClass("x-body-masked");
32659             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32660             this.mask.show();
32661         }
32662         this.constrainXY();
32663     },
32664
32665     // private
32666     animShow : function(){
32667         var b = Roo.get(this.animateTarget).getBox();
32668         this.proxy.setSize(b.width, b.height);
32669         this.proxy.setLocation(b.x, b.y);
32670         this.proxy.show();
32671         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32672                     true, .35, this.showEl.createDelegate(this));
32673     },
32674
32675     /**
32676      * Shows the dialog.
32677      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32678      * @return {Roo.BasicDialog} this
32679      */
32680     show : function(animateTarget){
32681         if (this.fireEvent("beforeshow", this) === false){
32682             return;
32683         }
32684         if(this.syncHeightBeforeShow){
32685             this.syncBodyHeight();
32686         }else if(this.firstShow){
32687             this.firstShow = false;
32688             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32689         }
32690         this.animateTarget = animateTarget || this.animateTarget;
32691         if(!this.el.isVisible()){
32692             this.beforeShow();
32693             if(this.animateTarget && Roo.get(this.animateTarget)){
32694                 this.animShow();
32695             }else{
32696                 this.showEl();
32697             }
32698         }
32699         return this;
32700     },
32701
32702     // private
32703     showEl : function(){
32704         this.proxy.hide();
32705         this.el.setXY(this.xy);
32706         this.el.show();
32707         this.adjustAssets(true);
32708         this.toFront();
32709         this.focus();
32710         // IE peekaboo bug - fix found by Dave Fenwick
32711         if(Roo.isIE){
32712             this.el.repaint();
32713         }
32714         this.fireEvent("show", this);
32715     },
32716
32717     /**
32718      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32719      * dialog itself will receive focus.
32720      */
32721     focus : function(){
32722         if(this.defaultButton){
32723             this.defaultButton.focus();
32724         }else{
32725             this.focusEl.focus();
32726         }
32727     },
32728
32729     // private
32730     constrainXY : function(){
32731         if(this.constraintoviewport !== false){
32732             if(!this.viewSize){
32733                 if(this.container){
32734                     var s = this.container.getSize();
32735                     this.viewSize = [s.width, s.height];
32736                 }else{
32737                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32738                 }
32739             }
32740             var s = Roo.get(this.container||document).getScroll();
32741
32742             var x = this.xy[0], y = this.xy[1];
32743             var w = this.size.width, h = this.size.height;
32744             var vw = this.viewSize[0], vh = this.viewSize[1];
32745             // only move it if it needs it
32746             var moved = false;
32747             // first validate right/bottom
32748             if(x + w > vw+s.left){
32749                 x = vw - w;
32750                 moved = true;
32751             }
32752             if(y + h > vh+s.top){
32753                 y = vh - h;
32754                 moved = true;
32755             }
32756             // then make sure top/left isn't negative
32757             if(x < s.left){
32758                 x = s.left;
32759                 moved = true;
32760             }
32761             if(y < s.top){
32762                 y = s.top;
32763                 moved = true;
32764             }
32765             if(moved){
32766                 // cache xy
32767                 this.xy = [x, y];
32768                 if(this.isVisible()){
32769                     this.el.setLocation(x, y);
32770                     this.adjustAssets();
32771                 }
32772             }
32773         }
32774     },
32775
32776     // private
32777     onDrag : function(){
32778         if(!this.proxyDrag){
32779             this.xy = this.el.getXY();
32780             this.adjustAssets();
32781         }
32782     },
32783
32784     // private
32785     adjustAssets : function(doShow){
32786         var x = this.xy[0], y = this.xy[1];
32787         var w = this.size.width, h = this.size.height;
32788         if(doShow === true){
32789             if(this.shadow){
32790                 this.shadow.show(this.el);
32791             }
32792             if(this.shim){
32793                 this.shim.show();
32794             }
32795         }
32796         if(this.shadow && this.shadow.isVisible()){
32797             this.shadow.show(this.el);
32798         }
32799         if(this.shim && this.shim.isVisible()){
32800             this.shim.setBounds(x, y, w, h);
32801         }
32802     },
32803
32804     // private
32805     adjustViewport : function(w, h){
32806         if(!w || !h){
32807             w = Roo.lib.Dom.getViewWidth();
32808             h = Roo.lib.Dom.getViewHeight();
32809         }
32810         // cache the size
32811         this.viewSize = [w, h];
32812         if(this.modal && this.mask.isVisible()){
32813             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32814             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32815         }
32816         if(this.isVisible()){
32817             this.constrainXY();
32818         }
32819     },
32820
32821     /**
32822      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32823      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32824      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32825      */
32826     destroy : function(removeEl){
32827         if(this.isVisible()){
32828             this.animateTarget = null;
32829             this.hide();
32830         }
32831         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32832         if(this.tabs){
32833             this.tabs.destroy(removeEl);
32834         }
32835         Roo.destroy(
32836              this.shim,
32837              this.proxy,
32838              this.resizer,
32839              this.close,
32840              this.mask
32841         );
32842         if(this.dd){
32843             this.dd.unreg();
32844         }
32845         if(this.buttons){
32846            for(var i = 0, len = this.buttons.length; i < len; i++){
32847                this.buttons[i].destroy();
32848            }
32849         }
32850         this.el.removeAllListeners();
32851         if(removeEl === true){
32852             this.el.update("");
32853             this.el.remove();
32854         }
32855         Roo.DialogManager.unregister(this);
32856     },
32857
32858     // private
32859     startMove : function(){
32860         if(this.proxyDrag){
32861             this.proxy.show();
32862         }
32863         if(this.constraintoviewport !== false){
32864             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32865         }
32866     },
32867
32868     // private
32869     endMove : function(){
32870         if(!this.proxyDrag){
32871             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32872         }else{
32873             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32874             this.proxy.hide();
32875         }
32876         this.refreshSize();
32877         this.adjustAssets();
32878         this.focus();
32879         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32880     },
32881
32882     /**
32883      * Brings this dialog to the front of any other visible dialogs
32884      * @return {Roo.BasicDialog} this
32885      */
32886     toFront : function(){
32887         Roo.DialogManager.bringToFront(this);
32888         return this;
32889     },
32890
32891     /**
32892      * Sends this dialog to the back (under) of any other visible dialogs
32893      * @return {Roo.BasicDialog} this
32894      */
32895     toBack : function(){
32896         Roo.DialogManager.sendToBack(this);
32897         return this;
32898     },
32899
32900     /**
32901      * Centers this dialog in the viewport
32902      * @return {Roo.BasicDialog} this
32903      */
32904     center : function(){
32905         var xy = this.el.getCenterXY(true);
32906         this.moveTo(xy[0], xy[1]);
32907         return this;
32908     },
32909
32910     /**
32911      * Moves the dialog's top-left corner to the specified point
32912      * @param {Number} x
32913      * @param {Number} y
32914      * @return {Roo.BasicDialog} this
32915      */
32916     moveTo : function(x, y){
32917         this.xy = [x,y];
32918         if(this.isVisible()){
32919             this.el.setXY(this.xy);
32920             this.adjustAssets();
32921         }
32922         return this;
32923     },
32924
32925     /**
32926      * Aligns the dialog to the specified element
32927      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32928      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32929      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32930      * @return {Roo.BasicDialog} this
32931      */
32932     alignTo : function(element, position, offsets){
32933         this.xy = this.el.getAlignToXY(element, position, offsets);
32934         if(this.isVisible()){
32935             this.el.setXY(this.xy);
32936             this.adjustAssets();
32937         }
32938         return this;
32939     },
32940
32941     /**
32942      * Anchors an element to another element and realigns it when the window is resized.
32943      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32944      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32945      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32946      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32947      * is a number, it is used as the buffer delay (defaults to 50ms).
32948      * @return {Roo.BasicDialog} this
32949      */
32950     anchorTo : function(el, alignment, offsets, monitorScroll){
32951         var action = function(){
32952             this.alignTo(el, alignment, offsets);
32953         };
32954         Roo.EventManager.onWindowResize(action, this);
32955         var tm = typeof monitorScroll;
32956         if(tm != 'undefined'){
32957             Roo.EventManager.on(window, 'scroll', action, this,
32958                 {buffer: tm == 'number' ? monitorScroll : 50});
32959         }
32960         action.call(this);
32961         return this;
32962     },
32963
32964     /**
32965      * Returns true if the dialog is visible
32966      * @return {Boolean}
32967      */
32968     isVisible : function(){
32969         return this.el.isVisible();
32970     },
32971
32972     // private
32973     animHide : function(callback){
32974         var b = Roo.get(this.animateTarget).getBox();
32975         this.proxy.show();
32976         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32977         this.el.hide();
32978         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32979                     this.hideEl.createDelegate(this, [callback]));
32980     },
32981
32982     /**
32983      * Hides the dialog.
32984      * @param {Function} callback (optional) Function to call when the dialog is hidden
32985      * @return {Roo.BasicDialog} this
32986      */
32987     hide : function(callback){
32988         if (this.fireEvent("beforehide", this) === false){
32989             return;
32990         }
32991         if(this.shadow){
32992             this.shadow.hide();
32993         }
32994         if(this.shim) {
32995           this.shim.hide();
32996         }
32997         // sometimes animateTarget seems to get set.. causing problems...
32998         // this just double checks..
32999         if(this.animateTarget && Roo.get(this.animateTarget)) {
33000            this.animHide(callback);
33001         }else{
33002             this.el.hide();
33003             this.hideEl(callback);
33004         }
33005         return this;
33006     },
33007
33008     // private
33009     hideEl : function(callback){
33010         this.proxy.hide();
33011         if(this.modal){
33012             this.mask.hide();
33013             Roo.get(document.body).removeClass("x-body-masked");
33014         }
33015         this.fireEvent("hide", this);
33016         if(typeof callback == "function"){
33017             callback();
33018         }
33019     },
33020
33021     // private
33022     hideAction : function(){
33023         this.setLeft("-10000px");
33024         this.setTop("-10000px");
33025         this.setStyle("visibility", "hidden");
33026     },
33027
33028     // private
33029     refreshSize : function(){
33030         this.size = this.el.getSize();
33031         this.xy = this.el.getXY();
33032         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33033     },
33034
33035     // private
33036     // z-index is managed by the DialogManager and may be overwritten at any time
33037     setZIndex : function(index){
33038         if(this.modal){
33039             this.mask.setStyle("z-index", index);
33040         }
33041         if(this.shim){
33042             this.shim.setStyle("z-index", ++index);
33043         }
33044         if(this.shadow){
33045             this.shadow.setZIndex(++index);
33046         }
33047         this.el.setStyle("z-index", ++index);
33048         if(this.proxy){
33049             this.proxy.setStyle("z-index", ++index);
33050         }
33051         if(this.resizer){
33052             this.resizer.proxy.setStyle("z-index", ++index);
33053         }
33054
33055         this.lastZIndex = index;
33056     },
33057
33058     /**
33059      * Returns the element for this dialog
33060      * @return {Roo.Element} The underlying dialog Element
33061      */
33062     getEl : function(){
33063         return this.el;
33064     }
33065 });
33066
33067 /**
33068  * @class Roo.DialogManager
33069  * Provides global access to BasicDialogs that have been created and
33070  * support for z-indexing (layering) multiple open dialogs.
33071  */
33072 Roo.DialogManager = function(){
33073     var list = {};
33074     var accessList = [];
33075     var front = null;
33076
33077     // private
33078     var sortDialogs = function(d1, d2){
33079         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33080     };
33081
33082     // private
33083     var orderDialogs = function(){
33084         accessList.sort(sortDialogs);
33085         var seed = Roo.DialogManager.zseed;
33086         for(var i = 0, len = accessList.length; i < len; i++){
33087             var dlg = accessList[i];
33088             if(dlg){
33089                 dlg.setZIndex(seed + (i*10));
33090             }
33091         }
33092     };
33093
33094     return {
33095         /**
33096          * The starting z-index for BasicDialogs (defaults to 9000)
33097          * @type Number The z-index value
33098          */
33099         zseed : 9000,
33100
33101         // private
33102         register : function(dlg){
33103             list[dlg.id] = dlg;
33104             accessList.push(dlg);
33105         },
33106
33107         // private
33108         unregister : function(dlg){
33109             delete list[dlg.id];
33110             var i=0;
33111             var len=0;
33112             if(!accessList.indexOf){
33113                 for(  i = 0, len = accessList.length; i < len; i++){
33114                     if(accessList[i] == dlg){
33115                         accessList.splice(i, 1);
33116                         return;
33117                     }
33118                 }
33119             }else{
33120                  i = accessList.indexOf(dlg);
33121                 if(i != -1){
33122                     accessList.splice(i, 1);
33123                 }
33124             }
33125         },
33126
33127         /**
33128          * Gets a registered dialog by id
33129          * @param {String/Object} id The id of the dialog or a dialog
33130          * @return {Roo.BasicDialog} this
33131          */
33132         get : function(id){
33133             return typeof id == "object" ? id : list[id];
33134         },
33135
33136         /**
33137          * Brings the specified dialog to the front
33138          * @param {String/Object} dlg The id of the dialog or a dialog
33139          * @return {Roo.BasicDialog} this
33140          */
33141         bringToFront : function(dlg){
33142             dlg = this.get(dlg);
33143             if(dlg != front){
33144                 front = dlg;
33145                 dlg._lastAccess = new Date().getTime();
33146                 orderDialogs();
33147             }
33148             return dlg;
33149         },
33150
33151         /**
33152          * Sends the specified dialog to the back
33153          * @param {String/Object} dlg The id of the dialog or a dialog
33154          * @return {Roo.BasicDialog} this
33155          */
33156         sendToBack : function(dlg){
33157             dlg = this.get(dlg);
33158             dlg._lastAccess = -(new Date().getTime());
33159             orderDialogs();
33160             return dlg;
33161         },
33162
33163         /**
33164          * Hides all dialogs
33165          */
33166         hideAll : function(){
33167             for(var id in list){
33168                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33169                     list[id].hide();
33170                 }
33171             }
33172         }
33173     };
33174 }();
33175
33176 /**
33177  * @class Roo.LayoutDialog
33178  * @extends Roo.BasicDialog
33179  * Dialog which provides adjustments for working with a layout in a Dialog.
33180  * Add your necessary layout config options to the dialog's config.<br>
33181  * Example usage (including a nested layout):
33182  * <pre><code>
33183 if(!dialog){
33184     dialog = new Roo.LayoutDialog("download-dlg", {
33185         modal: true,
33186         width:600,
33187         height:450,
33188         shadow:true,
33189         minWidth:500,
33190         minHeight:350,
33191         autoTabs:true,
33192         proxyDrag:true,
33193         // layout config merges with the dialog config
33194         center:{
33195             tabPosition: "top",
33196             alwaysShowTabs: true
33197         }
33198     });
33199     dialog.addKeyListener(27, dialog.hide, dialog);
33200     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
33201     dialog.addButton("Build It!", this.getDownload, this);
33202
33203     // we can even add nested layouts
33204     var innerLayout = new Roo.BorderLayout("dl-inner", {
33205         east: {
33206             initialSize: 200,
33207             autoScroll:true,
33208             split:true
33209         },
33210         center: {
33211             autoScroll:true
33212         }
33213     });
33214     innerLayout.beginUpdate();
33215     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33216     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33217     innerLayout.endUpdate(true);
33218
33219     var layout = dialog.getLayout();
33220     layout.beginUpdate();
33221     layout.add("center", new Roo.ContentPanel("standard-panel",
33222                         {title: "Download the Source", fitToFrame:true}));
33223     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33224                {title: "Build your own roo.js"}));
33225     layout.getRegion("center").showPanel(sp);
33226     layout.endUpdate();
33227 }
33228 </code></pre>
33229     * @constructor
33230     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33231     * @param {Object} config configuration options
33232   */
33233 Roo.LayoutDialog = function(el, cfg){
33234     
33235     var config=  cfg;
33236     if (typeof(cfg) == 'undefined') {
33237         config = Roo.apply({}, el);
33238         // not sure why we use documentElement here.. - it should always be body.
33239         // IE7 borks horribly if we use documentElement.
33240         // webkit also does not like documentElement - it creates a body element...
33241         el = Roo.get( document.body || document.documentElement ).createChild();
33242         //config.autoCreate = true;
33243     }
33244     
33245     
33246     config.autoTabs = false;
33247     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33248     this.body.setStyle({overflow:"hidden", position:"relative"});
33249     this.layout = new Roo.BorderLayout(this.body.dom, config);
33250     this.layout.monitorWindowResize = false;
33251     this.el.addClass("x-dlg-auto-layout");
33252     // fix case when center region overwrites center function
33253     this.center = Roo.BasicDialog.prototype.center;
33254     this.on("show", this.layout.layout, this.layout, true);
33255     if (config.items) {
33256         var xitems = config.items;
33257         delete config.items;
33258         Roo.each(xitems, this.addxtype, this);
33259     }
33260     
33261     
33262 };
33263 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33264     /**
33265      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33266      * @deprecated
33267      */
33268     endUpdate : function(){
33269         this.layout.endUpdate();
33270     },
33271
33272     /**
33273      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33274      *  @deprecated
33275      */
33276     beginUpdate : function(){
33277         this.layout.beginUpdate();
33278     },
33279
33280     /**
33281      * Get the BorderLayout for this dialog
33282      * @return {Roo.BorderLayout}
33283      */
33284     getLayout : function(){
33285         return this.layout;
33286     },
33287
33288     showEl : function(){
33289         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33290         if(Roo.isIE7){
33291             this.layout.layout();
33292         }
33293     },
33294
33295     // private
33296     // Use the syncHeightBeforeShow config option to control this automatically
33297     syncBodyHeight : function(){
33298         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33299         if(this.layout){this.layout.layout();}
33300     },
33301     
33302       /**
33303      * Add an xtype element (actually adds to the layout.)
33304      * @return {Object} xdata xtype object data.
33305      */
33306     
33307     addxtype : function(c) {
33308         return this.layout.addxtype(c);
33309     }
33310 });/*
33311  * Based on:
33312  * Ext JS Library 1.1.1
33313  * Copyright(c) 2006-2007, Ext JS, LLC.
33314  *
33315  * Originally Released Under LGPL - original licence link has changed is not relivant.
33316  *
33317  * Fork - LGPL
33318  * <script type="text/javascript">
33319  */
33320  
33321 /**
33322  * @class Roo.MessageBox
33323  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33324  * Example usage:
33325  *<pre><code>
33326 // Basic alert:
33327 Roo.Msg.alert('Status', 'Changes saved successfully.');
33328
33329 // Prompt for user data:
33330 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33331     if (btn == 'ok'){
33332         // process text value...
33333     }
33334 });
33335
33336 // Show a dialog using config options:
33337 Roo.Msg.show({
33338    title:'Save Changes?',
33339    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33340    buttons: Roo.Msg.YESNOCANCEL,
33341    fn: processResult,
33342    animEl: 'elId'
33343 });
33344 </code></pre>
33345  * @singleton
33346  */
33347 Roo.MessageBox = function(){
33348     var dlg, opt, mask, waitTimer;
33349     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33350     var buttons, activeTextEl, bwidth;
33351
33352     // private
33353     var handleButton = function(button){
33354         dlg.hide();
33355         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33356     };
33357
33358     // private
33359     var handleHide = function(){
33360         if(opt && opt.cls){
33361             dlg.el.removeClass(opt.cls);
33362         }
33363         if(waitTimer){
33364             Roo.TaskMgr.stop(waitTimer);
33365             waitTimer = null;
33366         }
33367     };
33368
33369     // private
33370     var updateButtons = function(b){
33371         var width = 0;
33372         if(!b){
33373             buttons["ok"].hide();
33374             buttons["cancel"].hide();
33375             buttons["yes"].hide();
33376             buttons["no"].hide();
33377             dlg.footer.dom.style.display = 'none';
33378             return width;
33379         }
33380         dlg.footer.dom.style.display = '';
33381         for(var k in buttons){
33382             if(typeof buttons[k] != "function"){
33383                 if(b[k]){
33384                     buttons[k].show();
33385                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33386                     width += buttons[k].el.getWidth()+15;
33387                 }else{
33388                     buttons[k].hide();
33389                 }
33390             }
33391         }
33392         return width;
33393     };
33394
33395     // private
33396     var handleEsc = function(d, k, e){
33397         if(opt && opt.closable !== false){
33398             dlg.hide();
33399         }
33400         if(e){
33401             e.stopEvent();
33402         }
33403     };
33404
33405     return {
33406         /**
33407          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33408          * @return {Roo.BasicDialog} The BasicDialog element
33409          */
33410         getDialog : function(){
33411            if(!dlg){
33412                 dlg = new Roo.BasicDialog("x-msg-box", {
33413                     autoCreate : true,
33414                     shadow: true,
33415                     draggable: true,
33416                     resizable:false,
33417                     constraintoviewport:false,
33418                     fixedcenter:true,
33419                     collapsible : false,
33420                     shim:true,
33421                     modal: true,
33422                     width:400, height:100,
33423                     buttonAlign:"center",
33424                     closeClick : function(){
33425                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33426                             handleButton("no");
33427                         }else{
33428                             handleButton("cancel");
33429                         }
33430                     }
33431                 });
33432                 dlg.on("hide", handleHide);
33433                 mask = dlg.mask;
33434                 dlg.addKeyListener(27, handleEsc);
33435                 buttons = {};
33436                 var bt = this.buttonText;
33437                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33438                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33439                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33440                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33441                 bodyEl = dlg.body.createChild({
33442
33443                     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>'
33444                 });
33445                 msgEl = bodyEl.dom.firstChild;
33446                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33447                 textboxEl.enableDisplayMode();
33448                 textboxEl.addKeyListener([10,13], function(){
33449                     if(dlg.isVisible() && opt && opt.buttons){
33450                         if(opt.buttons.ok){
33451                             handleButton("ok");
33452                         }else if(opt.buttons.yes){
33453                             handleButton("yes");
33454                         }
33455                     }
33456                 });
33457                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33458                 textareaEl.enableDisplayMode();
33459                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33460                 progressEl.enableDisplayMode();
33461                 var pf = progressEl.dom.firstChild;
33462                 if (pf) {
33463                     pp = Roo.get(pf.firstChild);
33464                     pp.setHeight(pf.offsetHeight);
33465                 }
33466                 
33467             }
33468             return dlg;
33469         },
33470
33471         /**
33472          * Updates the message box body text
33473          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33474          * the XHTML-compliant non-breaking space character '&amp;#160;')
33475          * @return {Roo.MessageBox} This message box
33476          */
33477         updateText : function(text){
33478             if(!dlg.isVisible() && !opt.width){
33479                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33480             }
33481             msgEl.innerHTML = text || '&#160;';
33482       
33483             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33484             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33485             var w = Math.max(
33486                     Math.min(opt.width || cw , this.maxWidth), 
33487                     Math.max(opt.minWidth || this.minWidth, bwidth)
33488             );
33489             if(opt.prompt){
33490                 activeTextEl.setWidth(w);
33491             }
33492             if(dlg.isVisible()){
33493                 dlg.fixedcenter = false;
33494             }
33495             // to big, make it scroll. = But as usual stupid IE does not support
33496             // !important..
33497             
33498             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33499                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33500                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33501             } else {
33502                 bodyEl.dom.style.height = '';
33503                 bodyEl.dom.style.overflowY = '';
33504             }
33505             if (cw > w) {
33506                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33507             } else {
33508                 bodyEl.dom.style.overflowX = '';
33509             }
33510             
33511             dlg.setContentSize(w, bodyEl.getHeight());
33512             if(dlg.isVisible()){
33513                 dlg.fixedcenter = true;
33514             }
33515             return this;
33516         },
33517
33518         /**
33519          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33520          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33521          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33522          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33523          * @return {Roo.MessageBox} This message box
33524          */
33525         updateProgress : function(value, text){
33526             if(text){
33527                 this.updateText(text);
33528             }
33529             if (pp) { // weird bug on my firefox - for some reason this is not defined
33530                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33531             }
33532             return this;
33533         },        
33534
33535         /**
33536          * Returns true if the message box is currently displayed
33537          * @return {Boolean} True if the message box is visible, else false
33538          */
33539         isVisible : function(){
33540             return dlg && dlg.isVisible();  
33541         },
33542
33543         /**
33544          * Hides the message box if it is displayed
33545          */
33546         hide : function(){
33547             if(this.isVisible()){
33548                 dlg.hide();
33549             }  
33550         },
33551
33552         /**
33553          * Displays a new message box, or reinitializes an existing message box, based on the config options
33554          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33555          * The following config object properties are supported:
33556          * <pre>
33557 Property    Type             Description
33558 ----------  ---------------  ------------------------------------------------------------------------------------
33559 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33560                                    closes (defaults to undefined)
33561 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33562                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33563 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33564                                    progress and wait dialogs will ignore this property and always hide the
33565                                    close button as they can only be closed programmatically.
33566 cls               String           A custom CSS class to apply to the message box element
33567 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33568                                    displayed (defaults to 75)
33569 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33570                                    function will be btn (the name of the button that was clicked, if applicable,
33571                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33572                                    Progress and wait dialogs will ignore this option since they do not respond to
33573                                    user actions and can only be closed programmatically, so any required function
33574                                    should be called by the same code after it closes the dialog.
33575 icon              String           A CSS class that provides a background image to be used as an icon for
33576                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33577 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33578 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33579 modal             Boolean          False to allow user interaction with the page while the message box is
33580                                    displayed (defaults to true)
33581 msg               String           A string that will replace the existing message box body text (defaults
33582                                    to the XHTML-compliant non-breaking space character '&#160;')
33583 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33584 progress          Boolean          True to display a progress bar (defaults to false)
33585 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33586 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33587 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33588 title             String           The title text
33589 value             String           The string value to set into the active textbox element if displayed
33590 wait              Boolean          True to display a progress bar (defaults to false)
33591 width             Number           The width of the dialog in pixels
33592 </pre>
33593          *
33594          * Example usage:
33595          * <pre><code>
33596 Roo.Msg.show({
33597    title: 'Address',
33598    msg: 'Please enter your address:',
33599    width: 300,
33600    buttons: Roo.MessageBox.OKCANCEL,
33601    multiline: true,
33602    fn: saveAddress,
33603    animEl: 'addAddressBtn'
33604 });
33605 </code></pre>
33606          * @param {Object} config Configuration options
33607          * @return {Roo.MessageBox} This message box
33608          */
33609         show : function(options)
33610         {
33611             
33612             // this causes nightmares if you show one dialog after another
33613             // especially on callbacks..
33614              
33615             if(this.isVisible()){
33616                 
33617                 this.hide();
33618                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33619                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33620                 Roo.log("New Dialog Message:" +  options.msg )
33621                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33622                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33623                 
33624             }
33625             var d = this.getDialog();
33626             opt = options;
33627             d.setTitle(opt.title || "&#160;");
33628             d.close.setDisplayed(opt.closable !== false);
33629             activeTextEl = textboxEl;
33630             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33631             if(opt.prompt){
33632                 if(opt.multiline){
33633                     textboxEl.hide();
33634                     textareaEl.show();
33635                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33636                         opt.multiline : this.defaultTextHeight);
33637                     activeTextEl = textareaEl;
33638                 }else{
33639                     textboxEl.show();
33640                     textareaEl.hide();
33641                 }
33642             }else{
33643                 textboxEl.hide();
33644                 textareaEl.hide();
33645             }
33646             progressEl.setDisplayed(opt.progress === true);
33647             this.updateProgress(0);
33648             activeTextEl.dom.value = opt.value || "";
33649             if(opt.prompt){
33650                 dlg.setDefaultButton(activeTextEl);
33651             }else{
33652                 var bs = opt.buttons;
33653                 var db = null;
33654                 if(bs && bs.ok){
33655                     db = buttons["ok"];
33656                 }else if(bs && bs.yes){
33657                     db = buttons["yes"];
33658                 }
33659                 dlg.setDefaultButton(db);
33660             }
33661             bwidth = updateButtons(opt.buttons);
33662             this.updateText(opt.msg);
33663             if(opt.cls){
33664                 d.el.addClass(opt.cls);
33665             }
33666             d.proxyDrag = opt.proxyDrag === true;
33667             d.modal = opt.modal !== false;
33668             d.mask = opt.modal !== false ? mask : false;
33669             if(!d.isVisible()){
33670                 // force it to the end of the z-index stack so it gets a cursor in FF
33671                 document.body.appendChild(dlg.el.dom);
33672                 d.animateTarget = null;
33673                 d.show(options.animEl);
33674             }
33675             return this;
33676         },
33677
33678         /**
33679          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33680          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33681          * and closing the message box when the process is complete.
33682          * @param {String} title The title bar text
33683          * @param {String} msg The message box body text
33684          * @return {Roo.MessageBox} This message box
33685          */
33686         progress : function(title, msg){
33687             this.show({
33688                 title : title,
33689                 msg : msg,
33690                 buttons: false,
33691                 progress:true,
33692                 closable:false,
33693                 minWidth: this.minProgressWidth,
33694                 modal : true
33695             });
33696             return this;
33697         },
33698
33699         /**
33700          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33701          * If a callback function is passed it will be called after the user clicks the button, and the
33702          * id of the button that was clicked will be passed as the only parameter to the callback
33703          * (could also be the top-right close button).
33704          * @param {String} title The title bar text
33705          * @param {String} msg The message box body text
33706          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33707          * @param {Object} scope (optional) The scope of the callback function
33708          * @return {Roo.MessageBox} This message box
33709          */
33710         alert : function(title, msg, fn, scope){
33711             this.show({
33712                 title : title,
33713                 msg : msg,
33714                 buttons: this.OK,
33715                 fn: fn,
33716                 scope : scope,
33717                 modal : true
33718             });
33719             return this;
33720         },
33721
33722         /**
33723          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33724          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33725          * You are responsible for closing the message box when the process is complete.
33726          * @param {String} msg The message box body text
33727          * @param {String} title (optional) The title bar text
33728          * @return {Roo.MessageBox} This message box
33729          */
33730         wait : function(msg, title){
33731             this.show({
33732                 title : title,
33733                 msg : msg,
33734                 buttons: false,
33735                 closable:false,
33736                 progress:true,
33737                 modal:true,
33738                 width:300,
33739                 wait:true
33740             });
33741             waitTimer = Roo.TaskMgr.start({
33742                 run: function(i){
33743                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33744                 },
33745                 interval: 1000
33746             });
33747             return this;
33748         },
33749
33750         /**
33751          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33752          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33753          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33754          * @param {String} title The title bar text
33755          * @param {String} msg The message box body text
33756          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33757          * @param {Object} scope (optional) The scope of the callback function
33758          * @return {Roo.MessageBox} This message box
33759          */
33760         confirm : function(title, msg, fn, scope){
33761             this.show({
33762                 title : title,
33763                 msg : msg,
33764                 buttons: this.YESNO,
33765                 fn: fn,
33766                 scope : scope,
33767                 modal : true
33768             });
33769             return this;
33770         },
33771
33772         /**
33773          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33774          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33775          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33776          * (could also be the top-right close button) and the text that was entered will be passed as the two
33777          * parameters to the callback.
33778          * @param {String} title The title bar text
33779          * @param {String} msg The message box body text
33780          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33781          * @param {Object} scope (optional) The scope of the callback function
33782          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33783          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33784          * @return {Roo.MessageBox} This message box
33785          */
33786         prompt : function(title, msg, fn, scope, multiline){
33787             this.show({
33788                 title : title,
33789                 msg : msg,
33790                 buttons: this.OKCANCEL,
33791                 fn: fn,
33792                 minWidth:250,
33793                 scope : scope,
33794                 prompt:true,
33795                 multiline: multiline,
33796                 modal : true
33797             });
33798             return this;
33799         },
33800
33801         /**
33802          * Button config that displays a single OK button
33803          * @type Object
33804          */
33805         OK : {ok:true},
33806         /**
33807          * Button config that displays Yes and No buttons
33808          * @type Object
33809          */
33810         YESNO : {yes:true, no:true},
33811         /**
33812          * Button config that displays OK and Cancel buttons
33813          * @type Object
33814          */
33815         OKCANCEL : {ok:true, cancel:true},
33816         /**
33817          * Button config that displays Yes, No and Cancel buttons
33818          * @type Object
33819          */
33820         YESNOCANCEL : {yes:true, no:true, cancel:true},
33821
33822         /**
33823          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33824          * @type Number
33825          */
33826         defaultTextHeight : 75,
33827         /**
33828          * The maximum width in pixels of the message box (defaults to 600)
33829          * @type Number
33830          */
33831         maxWidth : 600,
33832         /**
33833          * The minimum width in pixels of the message box (defaults to 100)
33834          * @type Number
33835          */
33836         minWidth : 100,
33837         /**
33838          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33839          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33840          * @type Number
33841          */
33842         minProgressWidth : 250,
33843         /**
33844          * An object containing the default button text strings that can be overriden for localized language support.
33845          * Supported properties are: ok, cancel, yes and no.
33846          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33847          * @type Object
33848          */
33849         buttonText : {
33850             ok : "OK",
33851             cancel : "Cancel",
33852             yes : "Yes",
33853             no : "No"
33854         }
33855     };
33856 }();
33857
33858 /**
33859  * Shorthand for {@link Roo.MessageBox}
33860  */
33861 Roo.Msg = Roo.MessageBox;/*
33862  * Based on:
33863  * Ext JS Library 1.1.1
33864  * Copyright(c) 2006-2007, Ext JS, LLC.
33865  *
33866  * Originally Released Under LGPL - original licence link has changed is not relivant.
33867  *
33868  * Fork - LGPL
33869  * <script type="text/javascript">
33870  */
33871 /**
33872  * @class Roo.QuickTips
33873  * Provides attractive and customizable tooltips for any element.
33874  * @singleton
33875  */
33876 Roo.QuickTips = function(){
33877     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33878     var ce, bd, xy, dd;
33879     var visible = false, disabled = true, inited = false;
33880     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33881     
33882     var onOver = function(e){
33883         if(disabled){
33884             return;
33885         }
33886         var t = e.getTarget();
33887         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33888             return;
33889         }
33890         if(ce && t == ce.el){
33891             clearTimeout(hideProc);
33892             return;
33893         }
33894         if(t && tagEls[t.id]){
33895             tagEls[t.id].el = t;
33896             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33897             return;
33898         }
33899         var ttp, et = Roo.fly(t);
33900         var ns = cfg.namespace;
33901         if(tm.interceptTitles && t.title){
33902             ttp = t.title;
33903             t.qtip = ttp;
33904             t.removeAttribute("title");
33905             e.preventDefault();
33906         }else{
33907             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33908         }
33909         if(ttp){
33910             showProc = show.defer(tm.showDelay, tm, [{
33911                 el: t, 
33912                 text: ttp.replace(/\\n/g,'<br/>'),
33913                 width: et.getAttributeNS(ns, cfg.width),
33914                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33915                 title: et.getAttributeNS(ns, cfg.title),
33916                     cls: et.getAttributeNS(ns, cfg.cls)
33917             }]);
33918         }
33919     };
33920     
33921     var onOut = function(e){
33922         clearTimeout(showProc);
33923         var t = e.getTarget();
33924         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33925             hideProc = setTimeout(hide, tm.hideDelay);
33926         }
33927     };
33928     
33929     var onMove = function(e){
33930         if(disabled){
33931             return;
33932         }
33933         xy = e.getXY();
33934         xy[1] += 18;
33935         if(tm.trackMouse && ce){
33936             el.setXY(xy);
33937         }
33938     };
33939     
33940     var onDown = function(e){
33941         clearTimeout(showProc);
33942         clearTimeout(hideProc);
33943         if(!e.within(el)){
33944             if(tm.hideOnClick){
33945                 hide();
33946                 tm.disable();
33947                 tm.enable.defer(100, tm);
33948             }
33949         }
33950     };
33951     
33952     var getPad = function(){
33953         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33954     };
33955
33956     var show = function(o){
33957         if(disabled){
33958             return;
33959         }
33960         clearTimeout(dismissProc);
33961         ce = o;
33962         if(removeCls){ // in case manually hidden
33963             el.removeClass(removeCls);
33964             removeCls = null;
33965         }
33966         if(ce.cls){
33967             el.addClass(ce.cls);
33968             removeCls = ce.cls;
33969         }
33970         if(ce.title){
33971             tipTitle.update(ce.title);
33972             tipTitle.show();
33973         }else{
33974             tipTitle.update('');
33975             tipTitle.hide();
33976         }
33977         el.dom.style.width  = tm.maxWidth+'px';
33978         //tipBody.dom.style.width = '';
33979         tipBodyText.update(o.text);
33980         var p = getPad(), w = ce.width;
33981         if(!w){
33982             var td = tipBodyText.dom;
33983             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33984             if(aw > tm.maxWidth){
33985                 w = tm.maxWidth;
33986             }else if(aw < tm.minWidth){
33987                 w = tm.minWidth;
33988             }else{
33989                 w = aw;
33990             }
33991         }
33992         //tipBody.setWidth(w);
33993         el.setWidth(parseInt(w, 10) + p);
33994         if(ce.autoHide === false){
33995             close.setDisplayed(true);
33996             if(dd){
33997                 dd.unlock();
33998             }
33999         }else{
34000             close.setDisplayed(false);
34001             if(dd){
34002                 dd.lock();
34003             }
34004         }
34005         if(xy){
34006             el.avoidY = xy[1]-18;
34007             el.setXY(xy);
34008         }
34009         if(tm.animate){
34010             el.setOpacity(.1);
34011             el.setStyle("visibility", "visible");
34012             el.fadeIn({callback: afterShow});
34013         }else{
34014             afterShow();
34015         }
34016     };
34017     
34018     var afterShow = function(){
34019         if(ce){
34020             el.show();
34021             esc.enable();
34022             if(tm.autoDismiss && ce.autoHide !== false){
34023                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34024             }
34025         }
34026     };
34027     
34028     var hide = function(noanim){
34029         clearTimeout(dismissProc);
34030         clearTimeout(hideProc);
34031         ce = null;
34032         if(el.isVisible()){
34033             esc.disable();
34034             if(noanim !== true && tm.animate){
34035                 el.fadeOut({callback: afterHide});
34036             }else{
34037                 afterHide();
34038             } 
34039         }
34040     };
34041     
34042     var afterHide = function(){
34043         el.hide();
34044         if(removeCls){
34045             el.removeClass(removeCls);
34046             removeCls = null;
34047         }
34048     };
34049     
34050     return {
34051         /**
34052         * @cfg {Number} minWidth
34053         * The minimum width of the quick tip (defaults to 40)
34054         */
34055        minWidth : 40,
34056         /**
34057         * @cfg {Number} maxWidth
34058         * The maximum width of the quick tip (defaults to 300)
34059         */
34060        maxWidth : 300,
34061         /**
34062         * @cfg {Boolean} interceptTitles
34063         * True to automatically use the element's DOM title value if available (defaults to false)
34064         */
34065        interceptTitles : false,
34066         /**
34067         * @cfg {Boolean} trackMouse
34068         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34069         */
34070        trackMouse : false,
34071         /**
34072         * @cfg {Boolean} hideOnClick
34073         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34074         */
34075        hideOnClick : true,
34076         /**
34077         * @cfg {Number} showDelay
34078         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
34079         */
34080        showDelay : 500,
34081         /**
34082         * @cfg {Number} hideDelay
34083         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
34084         */
34085        hideDelay : 200,
34086         /**
34087         * @cfg {Boolean} autoHide
34088         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
34089         * Used in conjunction with hideDelay.
34090         */
34091        autoHide : true,
34092         /**
34093         * @cfg {Boolean}
34094         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
34095         * (defaults to true).  Used in conjunction with autoDismissDelay.
34096         */
34097        autoDismiss : true,
34098         /**
34099         * @cfg {Number}
34100         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
34101         */
34102        autoDismissDelay : 5000,
34103        /**
34104         * @cfg {Boolean} animate
34105         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
34106         */
34107        animate : false,
34108
34109        /**
34110         * @cfg {String} title
34111         * Title text to display (defaults to '').  This can be any valid HTML markup.
34112         */
34113         title: '',
34114        /**
34115         * @cfg {String} text
34116         * Body text to display (defaults to '').  This can be any valid HTML markup.
34117         */
34118         text : '',
34119        /**
34120         * @cfg {String} cls
34121         * A CSS class to apply to the base quick tip element (defaults to '').
34122         */
34123         cls : '',
34124        /**
34125         * @cfg {Number} width
34126         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
34127         * minWidth or maxWidth.
34128         */
34129         width : null,
34130
34131     /**
34132      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
34133      * or display QuickTips in a page.
34134      */
34135        init : function(){
34136           tm = Roo.QuickTips;
34137           cfg = tm.tagConfig;
34138           if(!inited){
34139               if(!Roo.isReady){ // allow calling of init() before onReady
34140                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34141                   return;
34142               }
34143               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34144               el.fxDefaults = {stopFx: true};
34145               // maximum custom styling
34146               //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>');
34147               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>');              
34148               tipTitle = el.child('h3');
34149               tipTitle.enableDisplayMode("block");
34150               tipBody = el.child('div.x-tip-bd');
34151               tipBodyText = el.child('div.x-tip-bd-inner');
34152               //bdLeft = el.child('div.x-tip-bd-left');
34153               //bdRight = el.child('div.x-tip-bd-right');
34154               close = el.child('div.x-tip-close');
34155               close.enableDisplayMode("block");
34156               close.on("click", hide);
34157               var d = Roo.get(document);
34158               d.on("mousedown", onDown);
34159               d.on("mouseover", onOver);
34160               d.on("mouseout", onOut);
34161               d.on("mousemove", onMove);
34162               esc = d.addKeyListener(27, hide);
34163               esc.disable();
34164               if(Roo.dd.DD){
34165                   dd = el.initDD("default", null, {
34166                       onDrag : function(){
34167                           el.sync();  
34168                       }
34169                   });
34170                   dd.setHandleElId(tipTitle.id);
34171                   dd.lock();
34172               }
34173               inited = true;
34174           }
34175           this.enable(); 
34176        },
34177
34178     /**
34179      * Configures a new quick tip instance and assigns it to a target element.  The following config options
34180      * are supported:
34181      * <pre>
34182 Property    Type                   Description
34183 ----------  ---------------------  ------------------------------------------------------------------------
34184 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
34185      * </ul>
34186      * @param {Object} config The config object
34187      */
34188        register : function(config){
34189            var cs = config instanceof Array ? config : arguments;
34190            for(var i = 0, len = cs.length; i < len; i++) {
34191                var c = cs[i];
34192                var target = c.target;
34193                if(target){
34194                    if(target instanceof Array){
34195                        for(var j = 0, jlen = target.length; j < jlen; j++){
34196                            tagEls[target[j]] = c;
34197                        }
34198                    }else{
34199                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
34200                    }
34201                }
34202            }
34203        },
34204
34205     /**
34206      * Removes this quick tip from its element and destroys it.
34207      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34208      */
34209        unregister : function(el){
34210            delete tagEls[Roo.id(el)];
34211        },
34212
34213     /**
34214      * Enable this quick tip.
34215      */
34216        enable : function(){
34217            if(inited && disabled){
34218                locks.pop();
34219                if(locks.length < 1){
34220                    disabled = false;
34221                }
34222            }
34223        },
34224
34225     /**
34226      * Disable this quick tip.
34227      */
34228        disable : function(){
34229           disabled = true;
34230           clearTimeout(showProc);
34231           clearTimeout(hideProc);
34232           clearTimeout(dismissProc);
34233           if(ce){
34234               hide(true);
34235           }
34236           locks.push(1);
34237        },
34238
34239     /**
34240      * Returns true if the quick tip is enabled, else false.
34241      */
34242        isEnabled : function(){
34243             return !disabled;
34244        },
34245
34246         // private
34247        tagConfig : {
34248            namespace : "roo", // was ext?? this may break..
34249            alt_namespace : "ext",
34250            attribute : "qtip",
34251            width : "width",
34252            target : "target",
34253            title : "qtitle",
34254            hide : "hide",
34255            cls : "qclass"
34256        }
34257    };
34258 }();
34259
34260 // backwards compat
34261 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34262  * Based on:
34263  * Ext JS Library 1.1.1
34264  * Copyright(c) 2006-2007, Ext JS, LLC.
34265  *
34266  * Originally Released Under LGPL - original licence link has changed is not relivant.
34267  *
34268  * Fork - LGPL
34269  * <script type="text/javascript">
34270  */
34271  
34272
34273 /**
34274  * @class Roo.tree.TreePanel
34275  * @extends Roo.data.Tree
34276
34277  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34278  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34279  * @cfg {Boolean} enableDD true to enable drag and drop
34280  * @cfg {Boolean} enableDrag true to enable just drag
34281  * @cfg {Boolean} enableDrop true to enable just drop
34282  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34283  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34284  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34285  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34286  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34287  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34288  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34289  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34290  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34291  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34292  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34293  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34294  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34295  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34296  * @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>
34297  * @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>
34298  * 
34299  * @constructor
34300  * @param {String/HTMLElement/Element} el The container element
34301  * @param {Object} config
34302  */
34303 Roo.tree.TreePanel = function(el, config){
34304     var root = false;
34305     var loader = false;
34306     if (config.root) {
34307         root = config.root;
34308         delete config.root;
34309     }
34310     if (config.loader) {
34311         loader = config.loader;
34312         delete config.loader;
34313     }
34314     
34315     Roo.apply(this, config);
34316     Roo.tree.TreePanel.superclass.constructor.call(this);
34317     this.el = Roo.get(el);
34318     this.el.addClass('x-tree');
34319     //console.log(root);
34320     if (root) {
34321         this.setRootNode( Roo.factory(root, Roo.tree));
34322     }
34323     if (loader) {
34324         this.loader = Roo.factory(loader, Roo.tree);
34325     }
34326    /**
34327     * Read-only. The id of the container element becomes this TreePanel's id.
34328     */
34329     this.id = this.el.id;
34330     this.addEvents({
34331         /**
34332         * @event beforeload
34333         * Fires before a node is loaded, return false to cancel
34334         * @param {Node} node The node being loaded
34335         */
34336         "beforeload" : true,
34337         /**
34338         * @event load
34339         * Fires when a node is loaded
34340         * @param {Node} node The node that was loaded
34341         */
34342         "load" : true,
34343         /**
34344         * @event textchange
34345         * Fires when the text for a node is changed
34346         * @param {Node} node The node
34347         * @param {String} text The new text
34348         * @param {String} oldText The old text
34349         */
34350         "textchange" : true,
34351         /**
34352         * @event beforeexpand
34353         * Fires before a node is expanded, return false to cancel.
34354         * @param {Node} node The node
34355         * @param {Boolean} deep
34356         * @param {Boolean} anim
34357         */
34358         "beforeexpand" : true,
34359         /**
34360         * @event beforecollapse
34361         * Fires before a node is collapsed, return false to cancel.
34362         * @param {Node} node The node
34363         * @param {Boolean} deep
34364         * @param {Boolean} anim
34365         */
34366         "beforecollapse" : true,
34367         /**
34368         * @event expand
34369         * Fires when a node is expanded
34370         * @param {Node} node The node
34371         */
34372         "expand" : true,
34373         /**
34374         * @event disabledchange
34375         * Fires when the disabled status of a node changes
34376         * @param {Node} node The node
34377         * @param {Boolean} disabled
34378         */
34379         "disabledchange" : true,
34380         /**
34381         * @event collapse
34382         * Fires when a node is collapsed
34383         * @param {Node} node The node
34384         */
34385         "collapse" : true,
34386         /**
34387         * @event beforeclick
34388         * Fires before click processing on a node. Return false to cancel the default action.
34389         * @param {Node} node The node
34390         * @param {Roo.EventObject} e The event object
34391         */
34392         "beforeclick":true,
34393         /**
34394         * @event checkchange
34395         * Fires when a node with a checkbox's checked property changes
34396         * @param {Node} this This node
34397         * @param {Boolean} checked
34398         */
34399         "checkchange":true,
34400         /**
34401         * @event click
34402         * Fires when a node is clicked
34403         * @param {Node} node The node
34404         * @param {Roo.EventObject} e The event object
34405         */
34406         "click":true,
34407         /**
34408         * @event dblclick
34409         * Fires when a node is double clicked
34410         * @param {Node} node The node
34411         * @param {Roo.EventObject} e The event object
34412         */
34413         "dblclick":true,
34414         /**
34415         * @event contextmenu
34416         * Fires when a node is right clicked
34417         * @param {Node} node The node
34418         * @param {Roo.EventObject} e The event object
34419         */
34420         "contextmenu":true,
34421         /**
34422         * @event beforechildrenrendered
34423         * Fires right before the child nodes for a node are rendered
34424         * @param {Node} node The node
34425         */
34426         "beforechildrenrendered":true,
34427         /**
34428         * @event startdrag
34429         * Fires when a node starts being dragged
34430         * @param {Roo.tree.TreePanel} this
34431         * @param {Roo.tree.TreeNode} node
34432         * @param {event} e The raw browser event
34433         */ 
34434        "startdrag" : true,
34435        /**
34436         * @event enddrag
34437         * Fires when a drag operation is complete
34438         * @param {Roo.tree.TreePanel} this
34439         * @param {Roo.tree.TreeNode} node
34440         * @param {event} e The raw browser event
34441         */
34442        "enddrag" : true,
34443        /**
34444         * @event dragdrop
34445         * Fires when a dragged node is dropped on a valid DD target
34446         * @param {Roo.tree.TreePanel} this
34447         * @param {Roo.tree.TreeNode} node
34448         * @param {DD} dd The dd it was dropped on
34449         * @param {event} e The raw browser event
34450         */
34451        "dragdrop" : true,
34452        /**
34453         * @event beforenodedrop
34454         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34455         * passed to handlers has the following properties:<br />
34456         * <ul style="padding:5px;padding-left:16px;">
34457         * <li>tree - The TreePanel</li>
34458         * <li>target - The node being targeted for the drop</li>
34459         * <li>data - The drag data from the drag source</li>
34460         * <li>point - The point of the drop - append, above or below</li>
34461         * <li>source - The drag source</li>
34462         * <li>rawEvent - Raw mouse event</li>
34463         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34464         * to be inserted by setting them on this object.</li>
34465         * <li>cancel - Set this to true to cancel the drop.</li>
34466         * </ul>
34467         * @param {Object} dropEvent
34468         */
34469        "beforenodedrop" : true,
34470        /**
34471         * @event nodedrop
34472         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34473         * passed to handlers has the following properties:<br />
34474         * <ul style="padding:5px;padding-left:16px;">
34475         * <li>tree - The TreePanel</li>
34476         * <li>target - The node being targeted for the drop</li>
34477         * <li>data - The drag data from the drag source</li>
34478         * <li>point - The point of the drop - append, above or below</li>
34479         * <li>source - The drag source</li>
34480         * <li>rawEvent - Raw mouse event</li>
34481         * <li>dropNode - Dropped node(s).</li>
34482         * </ul>
34483         * @param {Object} dropEvent
34484         */
34485        "nodedrop" : true,
34486         /**
34487         * @event nodedragover
34488         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34489         * passed to handlers has the following properties:<br />
34490         * <ul style="padding:5px;padding-left:16px;">
34491         * <li>tree - The TreePanel</li>
34492         * <li>target - The node being targeted for the drop</li>
34493         * <li>data - The drag data from the drag source</li>
34494         * <li>point - The point of the drop - append, above or below</li>
34495         * <li>source - The drag source</li>
34496         * <li>rawEvent - Raw mouse event</li>
34497         * <li>dropNode - Drop node(s) provided by the source.</li>
34498         * <li>cancel - Set this to true to signal drop not allowed.</li>
34499         * </ul>
34500         * @param {Object} dragOverEvent
34501         */
34502        "nodedragover" : true,
34503        /**
34504         * @event appendnode
34505         * Fires when append node to the tree
34506         * @param {Roo.tree.TreePanel} this
34507         * @param {Roo.tree.TreeNode} node
34508         * @param {Number} index The index of the newly appended node
34509         */
34510        "appendnode" : true
34511         
34512     });
34513     if(this.singleExpand){
34514        this.on("beforeexpand", this.restrictExpand, this);
34515     }
34516     if (this.editor) {
34517         this.editor.tree = this;
34518         this.editor = Roo.factory(this.editor, Roo.tree);
34519     }
34520     
34521     if (this.selModel) {
34522         this.selModel = Roo.factory(this.selModel, Roo.tree);
34523     }
34524    
34525 };
34526 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34527     rootVisible : true,
34528     animate: Roo.enableFx,
34529     lines : true,
34530     enableDD : false,
34531     hlDrop : Roo.enableFx,
34532   
34533     renderer: false,
34534     
34535     rendererTip: false,
34536     // private
34537     restrictExpand : function(node){
34538         var p = node.parentNode;
34539         if(p){
34540             if(p.expandedChild && p.expandedChild.parentNode == p){
34541                 p.expandedChild.collapse();
34542             }
34543             p.expandedChild = node;
34544         }
34545     },
34546
34547     // private override
34548     setRootNode : function(node){
34549         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34550         if(!this.rootVisible){
34551             node.ui = new Roo.tree.RootTreeNodeUI(node);
34552         }
34553         return node;
34554     },
34555
34556     /**
34557      * Returns the container element for this TreePanel
34558      */
34559     getEl : function(){
34560         return this.el;
34561     },
34562
34563     /**
34564      * Returns the default TreeLoader for this TreePanel
34565      */
34566     getLoader : function(){
34567         return this.loader;
34568     },
34569
34570     /**
34571      * Expand all nodes
34572      */
34573     expandAll : function(){
34574         this.root.expand(true);
34575     },
34576
34577     /**
34578      * Collapse all nodes
34579      */
34580     collapseAll : function(){
34581         this.root.collapse(true);
34582     },
34583
34584     /**
34585      * Returns the selection model used by this TreePanel
34586      */
34587     getSelectionModel : function(){
34588         if(!this.selModel){
34589             this.selModel = new Roo.tree.DefaultSelectionModel();
34590         }
34591         return this.selModel;
34592     },
34593
34594     /**
34595      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34596      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34597      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34598      * @return {Array}
34599      */
34600     getChecked : function(a, startNode){
34601         startNode = startNode || this.root;
34602         var r = [];
34603         var f = function(){
34604             if(this.attributes.checked){
34605                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34606             }
34607         }
34608         startNode.cascade(f);
34609         return r;
34610     },
34611
34612     /**
34613      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34614      * @param {String} path
34615      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34616      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34617      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34618      */
34619     expandPath : function(path, attr, callback){
34620         attr = attr || "id";
34621         var keys = path.split(this.pathSeparator);
34622         var curNode = this.root;
34623         if(curNode.attributes[attr] != keys[1]){ // invalid root
34624             if(callback){
34625                 callback(false, null);
34626             }
34627             return;
34628         }
34629         var index = 1;
34630         var f = function(){
34631             if(++index == keys.length){
34632                 if(callback){
34633                     callback(true, curNode);
34634                 }
34635                 return;
34636             }
34637             var c = curNode.findChild(attr, keys[index]);
34638             if(!c){
34639                 if(callback){
34640                     callback(false, curNode);
34641                 }
34642                 return;
34643             }
34644             curNode = c;
34645             c.expand(false, false, f);
34646         };
34647         curNode.expand(false, false, f);
34648     },
34649
34650     /**
34651      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34652      * @param {String} path
34653      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34654      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34655      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34656      */
34657     selectPath : function(path, attr, callback){
34658         attr = attr || "id";
34659         var keys = path.split(this.pathSeparator);
34660         var v = keys.pop();
34661         if(keys.length > 0){
34662             var f = function(success, node){
34663                 if(success && node){
34664                     var n = node.findChild(attr, v);
34665                     if(n){
34666                         n.select();
34667                         if(callback){
34668                             callback(true, n);
34669                         }
34670                     }else if(callback){
34671                         callback(false, n);
34672                     }
34673                 }else{
34674                     if(callback){
34675                         callback(false, n);
34676                     }
34677                 }
34678             };
34679             this.expandPath(keys.join(this.pathSeparator), attr, f);
34680         }else{
34681             this.root.select();
34682             if(callback){
34683                 callback(true, this.root);
34684             }
34685         }
34686     },
34687
34688     getTreeEl : function(){
34689         return this.el;
34690     },
34691
34692     /**
34693      * Trigger rendering of this TreePanel
34694      */
34695     render : function(){
34696         if (this.innerCt) {
34697             return this; // stop it rendering more than once!!
34698         }
34699         
34700         this.innerCt = this.el.createChild({tag:"ul",
34701                cls:"x-tree-root-ct " +
34702                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34703
34704         if(this.containerScroll){
34705             Roo.dd.ScrollManager.register(this.el);
34706         }
34707         if((this.enableDD || this.enableDrop) && !this.dropZone){
34708            /**
34709             * The dropZone used by this tree if drop is enabled
34710             * @type Roo.tree.TreeDropZone
34711             */
34712              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34713                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34714            });
34715         }
34716         if((this.enableDD || this.enableDrag) && !this.dragZone){
34717            /**
34718             * The dragZone used by this tree if drag is enabled
34719             * @type Roo.tree.TreeDragZone
34720             */
34721             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34722                ddGroup: this.ddGroup || "TreeDD",
34723                scroll: this.ddScroll
34724            });
34725         }
34726         this.getSelectionModel().init(this);
34727         if (!this.root) {
34728             Roo.log("ROOT not set in tree");
34729             return this;
34730         }
34731         this.root.render();
34732         if(!this.rootVisible){
34733             this.root.renderChildren();
34734         }
34735         return this;
34736     }
34737 });/*
34738  * Based on:
34739  * Ext JS Library 1.1.1
34740  * Copyright(c) 2006-2007, Ext JS, LLC.
34741  *
34742  * Originally Released Under LGPL - original licence link has changed is not relivant.
34743  *
34744  * Fork - LGPL
34745  * <script type="text/javascript">
34746  */
34747  
34748
34749 /**
34750  * @class Roo.tree.DefaultSelectionModel
34751  * @extends Roo.util.Observable
34752  * The default single selection for a TreePanel.
34753  * @param {Object} cfg Configuration
34754  */
34755 Roo.tree.DefaultSelectionModel = function(cfg){
34756    this.selNode = null;
34757    
34758    
34759    
34760    this.addEvents({
34761        /**
34762         * @event selectionchange
34763         * Fires when the selected node changes
34764         * @param {DefaultSelectionModel} this
34765         * @param {TreeNode} node the new selection
34766         */
34767        "selectionchange" : true,
34768
34769        /**
34770         * @event beforeselect
34771         * Fires before the selected node changes, return false to cancel the change
34772         * @param {DefaultSelectionModel} this
34773         * @param {TreeNode} node the new selection
34774         * @param {TreeNode} node the old selection
34775         */
34776        "beforeselect" : true
34777    });
34778    
34779     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34780 };
34781
34782 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34783     init : function(tree){
34784         this.tree = tree;
34785         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34786         tree.on("click", this.onNodeClick, this);
34787     },
34788     
34789     onNodeClick : function(node, e){
34790         if (e.ctrlKey && this.selNode == node)  {
34791             this.unselect(node);
34792             return;
34793         }
34794         this.select(node);
34795     },
34796     
34797     /**
34798      * Select a node.
34799      * @param {TreeNode} node The node to select
34800      * @return {TreeNode} The selected node
34801      */
34802     select : function(node){
34803         var last = this.selNode;
34804         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34805             if(last){
34806                 last.ui.onSelectedChange(false);
34807             }
34808             this.selNode = node;
34809             node.ui.onSelectedChange(true);
34810             this.fireEvent("selectionchange", this, node, last);
34811         }
34812         return node;
34813     },
34814     
34815     /**
34816      * Deselect a node.
34817      * @param {TreeNode} node The node to unselect
34818      */
34819     unselect : function(node){
34820         if(this.selNode == node){
34821             this.clearSelections();
34822         }    
34823     },
34824     
34825     /**
34826      * Clear all selections
34827      */
34828     clearSelections : function(){
34829         var n = this.selNode;
34830         if(n){
34831             n.ui.onSelectedChange(false);
34832             this.selNode = null;
34833             this.fireEvent("selectionchange", this, null);
34834         }
34835         return n;
34836     },
34837     
34838     /**
34839      * Get the selected node
34840      * @return {TreeNode} The selected node
34841      */
34842     getSelectedNode : function(){
34843         return this.selNode;    
34844     },
34845     
34846     /**
34847      * Returns true if the node is selected
34848      * @param {TreeNode} node The node to check
34849      * @return {Boolean}
34850      */
34851     isSelected : function(node){
34852         return this.selNode == node;  
34853     },
34854
34855     /**
34856      * Selects the node above the selected node in the tree, intelligently walking the nodes
34857      * @return TreeNode The new selection
34858      */
34859     selectPrevious : function(){
34860         var s = this.selNode || this.lastSelNode;
34861         if(!s){
34862             return null;
34863         }
34864         var ps = s.previousSibling;
34865         if(ps){
34866             if(!ps.isExpanded() || ps.childNodes.length < 1){
34867                 return this.select(ps);
34868             } else{
34869                 var lc = ps.lastChild;
34870                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34871                     lc = lc.lastChild;
34872                 }
34873                 return this.select(lc);
34874             }
34875         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34876             return this.select(s.parentNode);
34877         }
34878         return null;
34879     },
34880
34881     /**
34882      * Selects the node above the selected node in the tree, intelligently walking the nodes
34883      * @return TreeNode The new selection
34884      */
34885     selectNext : function(){
34886         var s = this.selNode || this.lastSelNode;
34887         if(!s){
34888             return null;
34889         }
34890         if(s.firstChild && s.isExpanded()){
34891              return this.select(s.firstChild);
34892          }else if(s.nextSibling){
34893              return this.select(s.nextSibling);
34894          }else if(s.parentNode){
34895             var newS = null;
34896             s.parentNode.bubble(function(){
34897                 if(this.nextSibling){
34898                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34899                     return false;
34900                 }
34901             });
34902             return newS;
34903          }
34904         return null;
34905     },
34906
34907     onKeyDown : function(e){
34908         var s = this.selNode || this.lastSelNode;
34909         // undesirable, but required
34910         var sm = this;
34911         if(!s){
34912             return;
34913         }
34914         var k = e.getKey();
34915         switch(k){
34916              case e.DOWN:
34917                  e.stopEvent();
34918                  this.selectNext();
34919              break;
34920              case e.UP:
34921                  e.stopEvent();
34922                  this.selectPrevious();
34923              break;
34924              case e.RIGHT:
34925                  e.preventDefault();
34926                  if(s.hasChildNodes()){
34927                      if(!s.isExpanded()){
34928                          s.expand();
34929                      }else if(s.firstChild){
34930                          this.select(s.firstChild, e);
34931                      }
34932                  }
34933              break;
34934              case e.LEFT:
34935                  e.preventDefault();
34936                  if(s.hasChildNodes() && s.isExpanded()){
34937                      s.collapse();
34938                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34939                      this.select(s.parentNode, e);
34940                  }
34941              break;
34942         };
34943     }
34944 });
34945
34946 /**
34947  * @class Roo.tree.MultiSelectionModel
34948  * @extends Roo.util.Observable
34949  * Multi selection for a TreePanel.
34950  * @param {Object} cfg Configuration
34951  */
34952 Roo.tree.MultiSelectionModel = function(){
34953    this.selNodes = [];
34954    this.selMap = {};
34955    this.addEvents({
34956        /**
34957         * @event selectionchange
34958         * Fires when the selected nodes change
34959         * @param {MultiSelectionModel} this
34960         * @param {Array} nodes Array of the selected nodes
34961         */
34962        "selectionchange" : true
34963    });
34964    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34965    
34966 };
34967
34968 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34969     init : function(tree){
34970         this.tree = tree;
34971         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34972         tree.on("click", this.onNodeClick, this);
34973     },
34974     
34975     onNodeClick : function(node, e){
34976         this.select(node, e, e.ctrlKey);
34977     },
34978     
34979     /**
34980      * Select a node.
34981      * @param {TreeNode} node The node to select
34982      * @param {EventObject} e (optional) An event associated with the selection
34983      * @param {Boolean} keepExisting True to retain existing selections
34984      * @return {TreeNode} The selected node
34985      */
34986     select : function(node, e, keepExisting){
34987         if(keepExisting !== true){
34988             this.clearSelections(true);
34989         }
34990         if(this.isSelected(node)){
34991             this.lastSelNode = node;
34992             return node;
34993         }
34994         this.selNodes.push(node);
34995         this.selMap[node.id] = node;
34996         this.lastSelNode = node;
34997         node.ui.onSelectedChange(true);
34998         this.fireEvent("selectionchange", this, this.selNodes);
34999         return node;
35000     },
35001     
35002     /**
35003      * Deselect a node.
35004      * @param {TreeNode} node The node to unselect
35005      */
35006     unselect : function(node){
35007         if(this.selMap[node.id]){
35008             node.ui.onSelectedChange(false);
35009             var sn = this.selNodes;
35010             var index = -1;
35011             if(sn.indexOf){
35012                 index = sn.indexOf(node);
35013             }else{
35014                 for(var i = 0, len = sn.length; i < len; i++){
35015                     if(sn[i] == node){
35016                         index = i;
35017                         break;
35018                     }
35019                 }
35020             }
35021             if(index != -1){
35022                 this.selNodes.splice(index, 1);
35023             }
35024             delete this.selMap[node.id];
35025             this.fireEvent("selectionchange", this, this.selNodes);
35026         }
35027     },
35028     
35029     /**
35030      * Clear all selections
35031      */
35032     clearSelections : function(suppressEvent){
35033         var sn = this.selNodes;
35034         if(sn.length > 0){
35035             for(var i = 0, len = sn.length; i < len; i++){
35036                 sn[i].ui.onSelectedChange(false);
35037             }
35038             this.selNodes = [];
35039             this.selMap = {};
35040             if(suppressEvent !== true){
35041                 this.fireEvent("selectionchange", this, this.selNodes);
35042             }
35043         }
35044     },
35045     
35046     /**
35047      * Returns true if the node is selected
35048      * @param {TreeNode} node The node to check
35049      * @return {Boolean}
35050      */
35051     isSelected : function(node){
35052         return this.selMap[node.id] ? true : false;  
35053     },
35054     
35055     /**
35056      * Returns an array of the selected nodes
35057      * @return {Array}
35058      */
35059     getSelectedNodes : function(){
35060         return this.selNodes;    
35061     },
35062
35063     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35064
35065     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35066
35067     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35068 });/*
35069  * Based on:
35070  * Ext JS Library 1.1.1
35071  * Copyright(c) 2006-2007, Ext JS, LLC.
35072  *
35073  * Originally Released Under LGPL - original licence link has changed is not relivant.
35074  *
35075  * Fork - LGPL
35076  * <script type="text/javascript">
35077  */
35078  
35079 /**
35080  * @class Roo.tree.TreeNode
35081  * @extends Roo.data.Node
35082  * @cfg {String} text The text for this node
35083  * @cfg {Boolean} expanded true to start the node expanded
35084  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
35085  * @cfg {Boolean} allowDrop false if this node cannot be drop on
35086  * @cfg {Boolean} disabled true to start the node disabled
35087  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
35088  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
35089  * @cfg {String} cls A css class to be added to the node
35090  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
35091  * @cfg {String} href URL of the link used for the node (defaults to #)
35092  * @cfg {String} hrefTarget target frame for the link
35093  * @cfg {String} qtip An Ext QuickTip for the node
35094  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
35095  * @cfg {Boolean} singleClickExpand True for single click expand on this node
35096  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
35097  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
35098  * (defaults to undefined with no checkbox rendered)
35099  * @constructor
35100  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
35101  */
35102 Roo.tree.TreeNode = function(attributes){
35103     attributes = attributes || {};
35104     if(typeof attributes == "string"){
35105         attributes = {text: attributes};
35106     }
35107     this.childrenRendered = false;
35108     this.rendered = false;
35109     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
35110     this.expanded = attributes.expanded === true;
35111     this.isTarget = attributes.isTarget !== false;
35112     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
35113     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
35114
35115     /**
35116      * Read-only. The text for this node. To change it use setText().
35117      * @type String
35118      */
35119     this.text = attributes.text;
35120     /**
35121      * True if this node is disabled.
35122      * @type Boolean
35123      */
35124     this.disabled = attributes.disabled === true;
35125
35126     this.addEvents({
35127         /**
35128         * @event textchange
35129         * Fires when the text for this node is changed
35130         * @param {Node} this This node
35131         * @param {String} text The new text
35132         * @param {String} oldText The old text
35133         */
35134         "textchange" : true,
35135         /**
35136         * @event beforeexpand
35137         * Fires before this node is expanded, return false to cancel.
35138         * @param {Node} this This node
35139         * @param {Boolean} deep
35140         * @param {Boolean} anim
35141         */
35142         "beforeexpand" : true,
35143         /**
35144         * @event beforecollapse
35145         * Fires before this node is collapsed, return false to cancel.
35146         * @param {Node} this This node
35147         * @param {Boolean} deep
35148         * @param {Boolean} anim
35149         */
35150         "beforecollapse" : true,
35151         /**
35152         * @event expand
35153         * Fires when this node is expanded
35154         * @param {Node} this This node
35155         */
35156         "expand" : true,
35157         /**
35158         * @event disabledchange
35159         * Fires when the disabled status of this node changes
35160         * @param {Node} this This node
35161         * @param {Boolean} disabled
35162         */
35163         "disabledchange" : true,
35164         /**
35165         * @event collapse
35166         * Fires when this node is collapsed
35167         * @param {Node} this This node
35168         */
35169         "collapse" : true,
35170         /**
35171         * @event beforeclick
35172         * Fires before click processing. Return false to cancel the default action.
35173         * @param {Node} this This node
35174         * @param {Roo.EventObject} e The event object
35175         */
35176         "beforeclick":true,
35177         /**
35178         * @event checkchange
35179         * Fires when a node with a checkbox's checked property changes
35180         * @param {Node} this This node
35181         * @param {Boolean} checked
35182         */
35183         "checkchange":true,
35184         /**
35185         * @event click
35186         * Fires when this node is clicked
35187         * @param {Node} this This node
35188         * @param {Roo.EventObject} e The event object
35189         */
35190         "click":true,
35191         /**
35192         * @event dblclick
35193         * Fires when this node is double clicked
35194         * @param {Node} this This node
35195         * @param {Roo.EventObject} e The event object
35196         */
35197         "dblclick":true,
35198         /**
35199         * @event contextmenu
35200         * Fires when this node is right clicked
35201         * @param {Node} this This node
35202         * @param {Roo.EventObject} e The event object
35203         */
35204         "contextmenu":true,
35205         /**
35206         * @event beforechildrenrendered
35207         * Fires right before the child nodes for this node are rendered
35208         * @param {Node} this This node
35209         */
35210         "beforechildrenrendered":true
35211     });
35212
35213     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35214
35215     /**
35216      * Read-only. The UI for this node
35217      * @type TreeNodeUI
35218      */
35219     this.ui = new uiClass(this);
35220     
35221     // finally support items[]
35222     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35223         return;
35224     }
35225     
35226     
35227     Roo.each(this.attributes.items, function(c) {
35228         this.appendChild(Roo.factory(c,Roo.Tree));
35229     }, this);
35230     delete this.attributes.items;
35231     
35232     
35233     
35234 };
35235 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35236     preventHScroll: true,
35237     /**
35238      * Returns true if this node is expanded
35239      * @return {Boolean}
35240      */
35241     isExpanded : function(){
35242         return this.expanded;
35243     },
35244
35245     /**
35246      * Returns the UI object for this node
35247      * @return {TreeNodeUI}
35248      */
35249     getUI : function(){
35250         return this.ui;
35251     },
35252
35253     // private override
35254     setFirstChild : function(node){
35255         var of = this.firstChild;
35256         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35257         if(this.childrenRendered && of && node != of){
35258             of.renderIndent(true, true);
35259         }
35260         if(this.rendered){
35261             this.renderIndent(true, true);
35262         }
35263     },
35264
35265     // private override
35266     setLastChild : function(node){
35267         var ol = this.lastChild;
35268         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35269         if(this.childrenRendered && ol && node != ol){
35270             ol.renderIndent(true, true);
35271         }
35272         if(this.rendered){
35273             this.renderIndent(true, true);
35274         }
35275     },
35276
35277     // these methods are overridden to provide lazy rendering support
35278     // private override
35279     appendChild : function()
35280     {
35281         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35282         if(node && this.childrenRendered){
35283             node.render();
35284         }
35285         this.ui.updateExpandIcon();
35286         return node;
35287     },
35288
35289     // private override
35290     removeChild : function(node){
35291         this.ownerTree.getSelectionModel().unselect(node);
35292         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35293         // if it's been rendered remove dom node
35294         if(this.childrenRendered){
35295             node.ui.remove();
35296         }
35297         if(this.childNodes.length < 1){
35298             this.collapse(false, false);
35299         }else{
35300             this.ui.updateExpandIcon();
35301         }
35302         if(!this.firstChild) {
35303             this.childrenRendered = false;
35304         }
35305         return node;
35306     },
35307
35308     // private override
35309     insertBefore : function(node, refNode){
35310         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35311         if(newNode && refNode && this.childrenRendered){
35312             node.render();
35313         }
35314         this.ui.updateExpandIcon();
35315         return newNode;
35316     },
35317
35318     /**
35319      * Sets the text for this node
35320      * @param {String} text
35321      */
35322     setText : function(text){
35323         var oldText = this.text;
35324         this.text = text;
35325         this.attributes.text = text;
35326         if(this.rendered){ // event without subscribing
35327             this.ui.onTextChange(this, text, oldText);
35328         }
35329         this.fireEvent("textchange", this, text, oldText);
35330     },
35331
35332     /**
35333      * Triggers selection of this node
35334      */
35335     select : function(){
35336         this.getOwnerTree().getSelectionModel().select(this);
35337     },
35338
35339     /**
35340      * Triggers deselection of this node
35341      */
35342     unselect : function(){
35343         this.getOwnerTree().getSelectionModel().unselect(this);
35344     },
35345
35346     /**
35347      * Returns true if this node is selected
35348      * @return {Boolean}
35349      */
35350     isSelected : function(){
35351         return this.getOwnerTree().getSelectionModel().isSelected(this);
35352     },
35353
35354     /**
35355      * Expand this node.
35356      * @param {Boolean} deep (optional) True to expand all children as well
35357      * @param {Boolean} anim (optional) false to cancel the default animation
35358      * @param {Function} callback (optional) A callback to be called when
35359      * expanding this node completes (does not wait for deep expand to complete).
35360      * Called with 1 parameter, this node.
35361      */
35362     expand : function(deep, anim, callback){
35363         if(!this.expanded){
35364             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35365                 return;
35366             }
35367             if(!this.childrenRendered){
35368                 this.renderChildren();
35369             }
35370             this.expanded = true;
35371             
35372             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35373                 this.ui.animExpand(function(){
35374                     this.fireEvent("expand", this);
35375                     if(typeof callback == "function"){
35376                         callback(this);
35377                     }
35378                     if(deep === true){
35379                         this.expandChildNodes(true);
35380                     }
35381                 }.createDelegate(this));
35382                 return;
35383             }else{
35384                 this.ui.expand();
35385                 this.fireEvent("expand", this);
35386                 if(typeof callback == "function"){
35387                     callback(this);
35388                 }
35389             }
35390         }else{
35391            if(typeof callback == "function"){
35392                callback(this);
35393            }
35394         }
35395         if(deep === true){
35396             this.expandChildNodes(true);
35397         }
35398     },
35399
35400     isHiddenRoot : function(){
35401         return this.isRoot && !this.getOwnerTree().rootVisible;
35402     },
35403
35404     /**
35405      * Collapse this node.
35406      * @param {Boolean} deep (optional) True to collapse all children as well
35407      * @param {Boolean} anim (optional) false to cancel the default animation
35408      */
35409     collapse : function(deep, anim){
35410         if(this.expanded && !this.isHiddenRoot()){
35411             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35412                 return;
35413             }
35414             this.expanded = false;
35415             if((this.getOwnerTree().animate && anim !== false) || anim){
35416                 this.ui.animCollapse(function(){
35417                     this.fireEvent("collapse", this);
35418                     if(deep === true){
35419                         this.collapseChildNodes(true);
35420                     }
35421                 }.createDelegate(this));
35422                 return;
35423             }else{
35424                 this.ui.collapse();
35425                 this.fireEvent("collapse", this);
35426             }
35427         }
35428         if(deep === true){
35429             var cs = this.childNodes;
35430             for(var i = 0, len = cs.length; i < len; i++) {
35431                 cs[i].collapse(true, false);
35432             }
35433         }
35434     },
35435
35436     // private
35437     delayedExpand : function(delay){
35438         if(!this.expandProcId){
35439             this.expandProcId = this.expand.defer(delay, this);
35440         }
35441     },
35442
35443     // private
35444     cancelExpand : function(){
35445         if(this.expandProcId){
35446             clearTimeout(this.expandProcId);
35447         }
35448         this.expandProcId = false;
35449     },
35450
35451     /**
35452      * Toggles expanded/collapsed state of the node
35453      */
35454     toggle : function(){
35455         if(this.expanded){
35456             this.collapse();
35457         }else{
35458             this.expand();
35459         }
35460     },
35461
35462     /**
35463      * Ensures all parent nodes are expanded
35464      */
35465     ensureVisible : function(callback){
35466         var tree = this.getOwnerTree();
35467         tree.expandPath(this.parentNode.getPath(), false, function(){
35468             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35469             Roo.callback(callback);
35470         }.createDelegate(this));
35471     },
35472
35473     /**
35474      * Expand all child nodes
35475      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35476      */
35477     expandChildNodes : function(deep){
35478         var cs = this.childNodes;
35479         for(var i = 0, len = cs.length; i < len; i++) {
35480                 cs[i].expand(deep);
35481         }
35482     },
35483
35484     /**
35485      * Collapse all child nodes
35486      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35487      */
35488     collapseChildNodes : function(deep){
35489         var cs = this.childNodes;
35490         for(var i = 0, len = cs.length; i < len; i++) {
35491                 cs[i].collapse(deep);
35492         }
35493     },
35494
35495     /**
35496      * Disables this node
35497      */
35498     disable : function(){
35499         this.disabled = true;
35500         this.unselect();
35501         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35502             this.ui.onDisableChange(this, true);
35503         }
35504         this.fireEvent("disabledchange", this, true);
35505     },
35506
35507     /**
35508      * Enables this node
35509      */
35510     enable : function(){
35511         this.disabled = false;
35512         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35513             this.ui.onDisableChange(this, false);
35514         }
35515         this.fireEvent("disabledchange", this, false);
35516     },
35517
35518     // private
35519     renderChildren : function(suppressEvent){
35520         if(suppressEvent !== false){
35521             this.fireEvent("beforechildrenrendered", this);
35522         }
35523         var cs = this.childNodes;
35524         for(var i = 0, len = cs.length; i < len; i++){
35525             cs[i].render(true);
35526         }
35527         this.childrenRendered = true;
35528     },
35529
35530     // private
35531     sort : function(fn, scope){
35532         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35533         if(this.childrenRendered){
35534             var cs = this.childNodes;
35535             for(var i = 0, len = cs.length; i < len; i++){
35536                 cs[i].render(true);
35537             }
35538         }
35539     },
35540
35541     // private
35542     render : function(bulkRender){
35543         this.ui.render(bulkRender);
35544         if(!this.rendered){
35545             this.rendered = true;
35546             if(this.expanded){
35547                 this.expanded = false;
35548                 this.expand(false, false);
35549             }
35550         }
35551     },
35552
35553     // private
35554     renderIndent : function(deep, refresh){
35555         if(refresh){
35556             this.ui.childIndent = null;
35557         }
35558         this.ui.renderIndent();
35559         if(deep === true && this.childrenRendered){
35560             var cs = this.childNodes;
35561             for(var i = 0, len = cs.length; i < len; i++){
35562                 cs[i].renderIndent(true, refresh);
35563             }
35564         }
35565     }
35566 });/*
35567  * Based on:
35568  * Ext JS Library 1.1.1
35569  * Copyright(c) 2006-2007, Ext JS, LLC.
35570  *
35571  * Originally Released Under LGPL - original licence link has changed is not relivant.
35572  *
35573  * Fork - LGPL
35574  * <script type="text/javascript">
35575  */
35576  
35577 /**
35578  * @class Roo.tree.AsyncTreeNode
35579  * @extends Roo.tree.TreeNode
35580  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35581  * @constructor
35582  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35583  */
35584  Roo.tree.AsyncTreeNode = function(config){
35585     this.loaded = false;
35586     this.loading = false;
35587     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35588     /**
35589     * @event beforeload
35590     * Fires before this node is loaded, return false to cancel
35591     * @param {Node} this This node
35592     */
35593     this.addEvents({'beforeload':true, 'load': true});
35594     /**
35595     * @event load
35596     * Fires when this node is loaded
35597     * @param {Node} this This node
35598     */
35599     /**
35600      * The loader used by this node (defaults to using the tree's defined loader)
35601      * @type TreeLoader
35602      * @property loader
35603      */
35604 };
35605 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35606     expand : function(deep, anim, callback){
35607         if(this.loading){ // if an async load is already running, waiting til it's done
35608             var timer;
35609             var f = function(){
35610                 if(!this.loading){ // done loading
35611                     clearInterval(timer);
35612                     this.expand(deep, anim, callback);
35613                 }
35614             }.createDelegate(this);
35615             timer = setInterval(f, 200);
35616             return;
35617         }
35618         if(!this.loaded){
35619             if(this.fireEvent("beforeload", this) === false){
35620                 return;
35621             }
35622             this.loading = true;
35623             this.ui.beforeLoad(this);
35624             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35625             if(loader){
35626                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35627                 return;
35628             }
35629         }
35630         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35631     },
35632     
35633     /**
35634      * Returns true if this node is currently loading
35635      * @return {Boolean}
35636      */
35637     isLoading : function(){
35638         return this.loading;  
35639     },
35640     
35641     loadComplete : function(deep, anim, callback){
35642         this.loading = false;
35643         this.loaded = true;
35644         this.ui.afterLoad(this);
35645         this.fireEvent("load", this);
35646         this.expand(deep, anim, callback);
35647     },
35648     
35649     /**
35650      * Returns true if this node has been loaded
35651      * @return {Boolean}
35652      */
35653     isLoaded : function(){
35654         return this.loaded;
35655     },
35656     
35657     hasChildNodes : function(){
35658         if(!this.isLeaf() && !this.loaded){
35659             return true;
35660         }else{
35661             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35662         }
35663     },
35664
35665     /**
35666      * Trigger a reload for this node
35667      * @param {Function} callback
35668      */
35669     reload : function(callback){
35670         this.collapse(false, false);
35671         while(this.firstChild){
35672             this.removeChild(this.firstChild);
35673         }
35674         this.childrenRendered = false;
35675         this.loaded = false;
35676         if(this.isHiddenRoot()){
35677             this.expanded = false;
35678         }
35679         this.expand(false, false, callback);
35680     }
35681 });/*
35682  * Based on:
35683  * Ext JS Library 1.1.1
35684  * Copyright(c) 2006-2007, Ext JS, LLC.
35685  *
35686  * Originally Released Under LGPL - original licence link has changed is not relivant.
35687  *
35688  * Fork - LGPL
35689  * <script type="text/javascript">
35690  */
35691  
35692 /**
35693  * @class Roo.tree.TreeNodeUI
35694  * @constructor
35695  * @param {Object} node The node to render
35696  * The TreeNode UI implementation is separate from the
35697  * tree implementation. Unless you are customizing the tree UI,
35698  * you should never have to use this directly.
35699  */
35700 Roo.tree.TreeNodeUI = function(node){
35701     this.node = node;
35702     this.rendered = false;
35703     this.animating = false;
35704     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35705 };
35706
35707 Roo.tree.TreeNodeUI.prototype = {
35708     removeChild : function(node){
35709         if(this.rendered){
35710             this.ctNode.removeChild(node.ui.getEl());
35711         }
35712     },
35713
35714     beforeLoad : function(){
35715          this.addClass("x-tree-node-loading");
35716     },
35717
35718     afterLoad : function(){
35719          this.removeClass("x-tree-node-loading");
35720     },
35721
35722     onTextChange : function(node, text, oldText){
35723         if(this.rendered){
35724             this.textNode.innerHTML = text;
35725         }
35726     },
35727
35728     onDisableChange : function(node, state){
35729         this.disabled = state;
35730         if(state){
35731             this.addClass("x-tree-node-disabled");
35732         }else{
35733             this.removeClass("x-tree-node-disabled");
35734         }
35735     },
35736
35737     onSelectedChange : function(state){
35738         if(state){
35739             this.focus();
35740             this.addClass("x-tree-selected");
35741         }else{
35742             //this.blur();
35743             this.removeClass("x-tree-selected");
35744         }
35745     },
35746
35747     onMove : function(tree, node, oldParent, newParent, index, refNode){
35748         this.childIndent = null;
35749         if(this.rendered){
35750             var targetNode = newParent.ui.getContainer();
35751             if(!targetNode){//target not rendered
35752                 this.holder = document.createElement("div");
35753                 this.holder.appendChild(this.wrap);
35754                 return;
35755             }
35756             var insertBefore = refNode ? refNode.ui.getEl() : null;
35757             if(insertBefore){
35758                 targetNode.insertBefore(this.wrap, insertBefore);
35759             }else{
35760                 targetNode.appendChild(this.wrap);
35761             }
35762             this.node.renderIndent(true);
35763         }
35764     },
35765
35766     addClass : function(cls){
35767         if(this.elNode){
35768             Roo.fly(this.elNode).addClass(cls);
35769         }
35770     },
35771
35772     removeClass : function(cls){
35773         if(this.elNode){
35774             Roo.fly(this.elNode).removeClass(cls);
35775         }
35776     },
35777
35778     remove : function(){
35779         if(this.rendered){
35780             this.holder = document.createElement("div");
35781             this.holder.appendChild(this.wrap);
35782         }
35783     },
35784
35785     fireEvent : function(){
35786         return this.node.fireEvent.apply(this.node, arguments);
35787     },
35788
35789     initEvents : function(){
35790         this.node.on("move", this.onMove, this);
35791         var E = Roo.EventManager;
35792         var a = this.anchor;
35793
35794         var el = Roo.fly(a, '_treeui');
35795
35796         if(Roo.isOpera){ // opera render bug ignores the CSS
35797             el.setStyle("text-decoration", "none");
35798         }
35799
35800         el.on("click", this.onClick, this);
35801         el.on("dblclick", this.onDblClick, this);
35802
35803         if(this.checkbox){
35804             Roo.EventManager.on(this.checkbox,
35805                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35806         }
35807
35808         el.on("contextmenu", this.onContextMenu, this);
35809
35810         var icon = Roo.fly(this.iconNode);
35811         icon.on("click", this.onClick, this);
35812         icon.on("dblclick", this.onDblClick, this);
35813         icon.on("contextmenu", this.onContextMenu, this);
35814         E.on(this.ecNode, "click", this.ecClick, this, true);
35815
35816         if(this.node.disabled){
35817             this.addClass("x-tree-node-disabled");
35818         }
35819         if(this.node.hidden){
35820             this.addClass("x-tree-node-disabled");
35821         }
35822         var ot = this.node.getOwnerTree();
35823         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35824         if(dd && (!this.node.isRoot || ot.rootVisible)){
35825             Roo.dd.Registry.register(this.elNode, {
35826                 node: this.node,
35827                 handles: this.getDDHandles(),
35828                 isHandle: false
35829             });
35830         }
35831     },
35832
35833     getDDHandles : function(){
35834         return [this.iconNode, this.textNode];
35835     },
35836
35837     hide : function(){
35838         if(this.rendered){
35839             this.wrap.style.display = "none";
35840         }
35841     },
35842
35843     show : function(){
35844         if(this.rendered){
35845             this.wrap.style.display = "";
35846         }
35847     },
35848
35849     onContextMenu : function(e){
35850         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35851             e.preventDefault();
35852             this.focus();
35853             this.fireEvent("contextmenu", this.node, e);
35854         }
35855     },
35856
35857     onClick : function(e){
35858         if(this.dropping){
35859             e.stopEvent();
35860             return;
35861         }
35862         if(this.fireEvent("beforeclick", this.node, e) !== false){
35863             if(!this.disabled && this.node.attributes.href){
35864                 this.fireEvent("click", this.node, e);
35865                 return;
35866             }
35867             e.preventDefault();
35868             if(this.disabled){
35869                 return;
35870             }
35871
35872             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35873                 this.node.toggle();
35874             }
35875
35876             this.fireEvent("click", this.node, e);
35877         }else{
35878             e.stopEvent();
35879         }
35880     },
35881
35882     onDblClick : function(e){
35883         e.preventDefault();
35884         if(this.disabled){
35885             return;
35886         }
35887         if(this.checkbox){
35888             this.toggleCheck();
35889         }
35890         if(!this.animating && this.node.hasChildNodes()){
35891             this.node.toggle();
35892         }
35893         this.fireEvent("dblclick", this.node, e);
35894     },
35895
35896     onCheckChange : function(){
35897         var checked = this.checkbox.checked;
35898         this.node.attributes.checked = checked;
35899         this.fireEvent('checkchange', this.node, checked);
35900     },
35901
35902     ecClick : function(e){
35903         if(!this.animating && this.node.hasChildNodes()){
35904             this.node.toggle();
35905         }
35906     },
35907
35908     startDrop : function(){
35909         this.dropping = true;
35910     },
35911
35912     // delayed drop so the click event doesn't get fired on a drop
35913     endDrop : function(){
35914        setTimeout(function(){
35915            this.dropping = false;
35916        }.createDelegate(this), 50);
35917     },
35918
35919     expand : function(){
35920         this.updateExpandIcon();
35921         this.ctNode.style.display = "";
35922     },
35923
35924     focus : function(){
35925         if(!this.node.preventHScroll){
35926             try{this.anchor.focus();
35927             }catch(e){}
35928         }else if(!Roo.isIE){
35929             try{
35930                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35931                 var l = noscroll.scrollLeft;
35932                 this.anchor.focus();
35933                 noscroll.scrollLeft = l;
35934             }catch(e){}
35935         }
35936     },
35937
35938     toggleCheck : function(value){
35939         var cb = this.checkbox;
35940         if(cb){
35941             cb.checked = (value === undefined ? !cb.checked : value);
35942         }
35943     },
35944
35945     blur : function(){
35946         try{
35947             this.anchor.blur();
35948         }catch(e){}
35949     },
35950
35951     animExpand : function(callback){
35952         var ct = Roo.get(this.ctNode);
35953         ct.stopFx();
35954         if(!this.node.hasChildNodes()){
35955             this.updateExpandIcon();
35956             this.ctNode.style.display = "";
35957             Roo.callback(callback);
35958             return;
35959         }
35960         this.animating = true;
35961         this.updateExpandIcon();
35962
35963         ct.slideIn('t', {
35964            callback : function(){
35965                this.animating = false;
35966                Roo.callback(callback);
35967             },
35968             scope: this,
35969             duration: this.node.ownerTree.duration || .25
35970         });
35971     },
35972
35973     highlight : function(){
35974         var tree = this.node.getOwnerTree();
35975         Roo.fly(this.wrap).highlight(
35976             tree.hlColor || "C3DAF9",
35977             {endColor: tree.hlBaseColor}
35978         );
35979     },
35980
35981     collapse : function(){
35982         this.updateExpandIcon();
35983         this.ctNode.style.display = "none";
35984     },
35985
35986     animCollapse : function(callback){
35987         var ct = Roo.get(this.ctNode);
35988         ct.enableDisplayMode('block');
35989         ct.stopFx();
35990
35991         this.animating = true;
35992         this.updateExpandIcon();
35993
35994         ct.slideOut('t', {
35995             callback : function(){
35996                this.animating = false;
35997                Roo.callback(callback);
35998             },
35999             scope: this,
36000             duration: this.node.ownerTree.duration || .25
36001         });
36002     },
36003
36004     getContainer : function(){
36005         return this.ctNode;
36006     },
36007
36008     getEl : function(){
36009         return this.wrap;
36010     },
36011
36012     appendDDGhost : function(ghostNode){
36013         ghostNode.appendChild(this.elNode.cloneNode(true));
36014     },
36015
36016     getDDRepairXY : function(){
36017         return Roo.lib.Dom.getXY(this.iconNode);
36018     },
36019
36020     onRender : function(){
36021         this.render();
36022     },
36023
36024     render : function(bulkRender){
36025         var n = this.node, a = n.attributes;
36026         var targetNode = n.parentNode ?
36027               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36028
36029         if(!this.rendered){
36030             this.rendered = true;
36031
36032             this.renderElements(n, a, targetNode, bulkRender);
36033
36034             if(a.qtip){
36035                if(this.textNode.setAttributeNS){
36036                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36037                    if(a.qtipTitle){
36038                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36039                    }
36040                }else{
36041                    this.textNode.setAttribute("ext:qtip", a.qtip);
36042                    if(a.qtipTitle){
36043                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36044                    }
36045                }
36046             }else if(a.qtipCfg){
36047                 a.qtipCfg.target = Roo.id(this.textNode);
36048                 Roo.QuickTips.register(a.qtipCfg);
36049             }
36050             this.initEvents();
36051             if(!this.node.expanded){
36052                 this.updateExpandIcon();
36053             }
36054         }else{
36055             if(bulkRender === true) {
36056                 targetNode.appendChild(this.wrap);
36057             }
36058         }
36059     },
36060
36061     renderElements : function(n, a, targetNode, bulkRender)
36062     {
36063         // add some indent caching, this helps performance when rendering a large tree
36064         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36065         var t = n.getOwnerTree();
36066         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36067         if (typeof(n.attributes.html) != 'undefined') {
36068             txt = n.attributes.html;
36069         }
36070         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36071         var cb = typeof a.checked == 'boolean';
36072         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36073         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36074             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36075             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36076             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
36077             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
36078             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
36079              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
36080                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
36081             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36082             "</li>"];
36083
36084         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36085             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36086                                 n.nextSibling.ui.getEl(), buf.join(""));
36087         }else{
36088             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36089         }
36090
36091         this.elNode = this.wrap.childNodes[0];
36092         this.ctNode = this.wrap.childNodes[1];
36093         var cs = this.elNode.childNodes;
36094         this.indentNode = cs[0];
36095         this.ecNode = cs[1];
36096         this.iconNode = cs[2];
36097         var index = 3;
36098         if(cb){
36099             this.checkbox = cs[3];
36100             index++;
36101         }
36102         this.anchor = cs[index];
36103         this.textNode = cs[index].firstChild;
36104     },
36105
36106     getAnchor : function(){
36107         return this.anchor;
36108     },
36109
36110     getTextEl : function(){
36111         return this.textNode;
36112     },
36113
36114     getIconEl : function(){
36115         return this.iconNode;
36116     },
36117
36118     isChecked : function(){
36119         return this.checkbox ? this.checkbox.checked : false;
36120     },
36121
36122     updateExpandIcon : function(){
36123         if(this.rendered){
36124             var n = this.node, c1, c2;
36125             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
36126             var hasChild = n.hasChildNodes();
36127             if(hasChild){
36128                 if(n.expanded){
36129                     cls += "-minus";
36130                     c1 = "x-tree-node-collapsed";
36131                     c2 = "x-tree-node-expanded";
36132                 }else{
36133                     cls += "-plus";
36134                     c1 = "x-tree-node-expanded";
36135                     c2 = "x-tree-node-collapsed";
36136                 }
36137                 if(this.wasLeaf){
36138                     this.removeClass("x-tree-node-leaf");
36139                     this.wasLeaf = false;
36140                 }
36141                 if(this.c1 != c1 || this.c2 != c2){
36142                     Roo.fly(this.elNode).replaceClass(c1, c2);
36143                     this.c1 = c1; this.c2 = c2;
36144                 }
36145             }else{
36146                 // this changes non-leafs into leafs if they have no children.
36147                 // it's not very rational behaviour..
36148                 
36149                 if(!this.wasLeaf && this.node.leaf){
36150                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36151                     delete this.c1;
36152                     delete this.c2;
36153                     this.wasLeaf = true;
36154                 }
36155             }
36156             var ecc = "x-tree-ec-icon "+cls;
36157             if(this.ecc != ecc){
36158                 this.ecNode.className = ecc;
36159                 this.ecc = ecc;
36160             }
36161         }
36162     },
36163
36164     getChildIndent : function(){
36165         if(!this.childIndent){
36166             var buf = [];
36167             var p = this.node;
36168             while(p){
36169                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
36170                     if(!p.isLast()) {
36171                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
36172                     } else {
36173                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
36174                     }
36175                 }
36176                 p = p.parentNode;
36177             }
36178             this.childIndent = buf.join("");
36179         }
36180         return this.childIndent;
36181     },
36182
36183     renderIndent : function(){
36184         if(this.rendered){
36185             var indent = "";
36186             var p = this.node.parentNode;
36187             if(p){
36188                 indent = p.ui.getChildIndent();
36189             }
36190             if(this.indentMarkup != indent){ // don't rerender if not required
36191                 this.indentNode.innerHTML = indent;
36192                 this.indentMarkup = indent;
36193             }
36194             this.updateExpandIcon();
36195         }
36196     }
36197 };
36198
36199 Roo.tree.RootTreeNodeUI = function(){
36200     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
36201 };
36202 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
36203     render : function(){
36204         if(!this.rendered){
36205             var targetNode = this.node.ownerTree.innerCt.dom;
36206             this.node.expanded = true;
36207             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36208             this.wrap = this.ctNode = targetNode.firstChild;
36209         }
36210     },
36211     collapse : function(){
36212     },
36213     expand : function(){
36214     }
36215 });/*
36216  * Based on:
36217  * Ext JS Library 1.1.1
36218  * Copyright(c) 2006-2007, Ext JS, LLC.
36219  *
36220  * Originally Released Under LGPL - original licence link has changed is not relivant.
36221  *
36222  * Fork - LGPL
36223  * <script type="text/javascript">
36224  */
36225 /**
36226  * @class Roo.tree.TreeLoader
36227  * @extends Roo.util.Observable
36228  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36229  * nodes from a specified URL. The response must be a javascript Array definition
36230  * who's elements are node definition objects. eg:
36231  * <pre><code>
36232 {  success : true,
36233    data :      [
36234    
36235     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36236     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36237     ]
36238 }
36239
36240
36241 </code></pre>
36242  * <br><br>
36243  * The old style respose with just an array is still supported, but not recommended.
36244  * <br><br>
36245  *
36246  * A server request is sent, and child nodes are loaded only when a node is expanded.
36247  * The loading node's id is passed to the server under the parameter name "node" to
36248  * enable the server to produce the correct child nodes.
36249  * <br><br>
36250  * To pass extra parameters, an event handler may be attached to the "beforeload"
36251  * event, and the parameters specified in the TreeLoader's baseParams property:
36252  * <pre><code>
36253     myTreeLoader.on("beforeload", function(treeLoader, node) {
36254         this.baseParams.category = node.attributes.category;
36255     }, this);
36256     
36257 </code></pre>
36258  *
36259  * This would pass an HTTP parameter called "category" to the server containing
36260  * the value of the Node's "category" attribute.
36261  * @constructor
36262  * Creates a new Treeloader.
36263  * @param {Object} config A config object containing config properties.
36264  */
36265 Roo.tree.TreeLoader = function(config){
36266     this.baseParams = {};
36267     this.requestMethod = "POST";
36268     Roo.apply(this, config);
36269
36270     this.addEvents({
36271     
36272         /**
36273          * @event beforeload
36274          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36275          * @param {Object} This TreeLoader object.
36276          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36277          * @param {Object} callback The callback function specified in the {@link #load} call.
36278          */
36279         beforeload : true,
36280         /**
36281          * @event load
36282          * Fires when the node has been successfuly loaded.
36283          * @param {Object} This TreeLoader object.
36284          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36285          * @param {Object} response The response object containing the data from the server.
36286          */
36287         load : true,
36288         /**
36289          * @event loadexception
36290          * Fires if the network request failed.
36291          * @param {Object} This TreeLoader object.
36292          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36293          * @param {Object} response The response object containing the data from the server.
36294          */
36295         loadexception : true,
36296         /**
36297          * @event create
36298          * Fires before a node is created, enabling you to return custom Node types 
36299          * @param {Object} This TreeLoader object.
36300          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36301          */
36302         create : true
36303     });
36304
36305     Roo.tree.TreeLoader.superclass.constructor.call(this);
36306 };
36307
36308 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36309     /**
36310     * @cfg {String} dataUrl The URL from which to request a Json string which
36311     * specifies an array of node definition object representing the child nodes
36312     * to be loaded.
36313     */
36314     /**
36315     * @cfg {String} requestMethod either GET or POST
36316     * defaults to POST (due to BC)
36317     * to be loaded.
36318     */
36319     /**
36320     * @cfg {Object} baseParams (optional) An object containing properties which
36321     * specify HTTP parameters to be passed to each request for child nodes.
36322     */
36323     /**
36324     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36325     * created by this loader. If the attributes sent by the server have an attribute in this object,
36326     * they take priority.
36327     */
36328     /**
36329     * @cfg {Object} uiProviders (optional) An object containing properties which
36330     * 
36331     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36332     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36333     * <i>uiProvider</i> attribute of a returned child node is a string rather
36334     * than a reference to a TreeNodeUI implementation, this that string value
36335     * is used as a property name in the uiProviders object. You can define the provider named
36336     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36337     */
36338     uiProviders : {},
36339
36340     /**
36341     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36342     * child nodes before loading.
36343     */
36344     clearOnLoad : true,
36345
36346     /**
36347     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36348     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36349     * Grid query { data : [ .....] }
36350     */
36351     
36352     root : false,
36353      /**
36354     * @cfg {String} queryParam (optional) 
36355     * Name of the query as it will be passed on the querystring (defaults to 'node')
36356     * eg. the request will be ?node=[id]
36357     */
36358     
36359     
36360     queryParam: false,
36361     
36362     /**
36363      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36364      * This is called automatically when a node is expanded, but may be used to reload
36365      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36366      * @param {Roo.tree.TreeNode} node
36367      * @param {Function} callback
36368      */
36369     load : function(node, callback){
36370         if(this.clearOnLoad){
36371             while(node.firstChild){
36372                 node.removeChild(node.firstChild);
36373             }
36374         }
36375         if(node.attributes.children){ // preloaded json children
36376             var cs = node.attributes.children;
36377             for(var i = 0, len = cs.length; i < len; i++){
36378                 node.appendChild(this.createNode(cs[i]));
36379             }
36380             if(typeof callback == "function"){
36381                 callback();
36382             }
36383         }else if(this.dataUrl){
36384             this.requestData(node, callback);
36385         }
36386     },
36387
36388     getParams: function(node){
36389         var buf = [], bp = this.baseParams;
36390         for(var key in bp){
36391             if(typeof bp[key] != "function"){
36392                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36393             }
36394         }
36395         var n = this.queryParam === false ? 'node' : this.queryParam;
36396         buf.push(n + "=", encodeURIComponent(node.id));
36397         return buf.join("");
36398     },
36399
36400     requestData : function(node, callback){
36401         if(this.fireEvent("beforeload", this, node, callback) !== false){
36402             this.transId = Roo.Ajax.request({
36403                 method:this.requestMethod,
36404                 url: this.dataUrl||this.url,
36405                 success: this.handleResponse,
36406                 failure: this.handleFailure,
36407                 scope: this,
36408                 argument: {callback: callback, node: node},
36409                 params: this.getParams(node)
36410             });
36411         }else{
36412             // if the load is cancelled, make sure we notify
36413             // the node that we are done
36414             if(typeof callback == "function"){
36415                 callback();
36416             }
36417         }
36418     },
36419
36420     isLoading : function(){
36421         return this.transId ? true : false;
36422     },
36423
36424     abort : function(){
36425         if(this.isLoading()){
36426             Roo.Ajax.abort(this.transId);
36427         }
36428     },
36429
36430     // private
36431     createNode : function(attr)
36432     {
36433         // apply baseAttrs, nice idea Corey!
36434         if(this.baseAttrs){
36435             Roo.applyIf(attr, this.baseAttrs);
36436         }
36437         if(this.applyLoader !== false){
36438             attr.loader = this;
36439         }
36440         // uiProvider = depreciated..
36441         
36442         if(typeof(attr.uiProvider) == 'string'){
36443            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36444                 /**  eval:var:attr */ eval(attr.uiProvider);
36445         }
36446         if(typeof(this.uiProviders['default']) != 'undefined') {
36447             attr.uiProvider = this.uiProviders['default'];
36448         }
36449         
36450         this.fireEvent('create', this, attr);
36451         
36452         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36453         return(attr.leaf ?
36454                         new Roo.tree.TreeNode(attr) :
36455                         new Roo.tree.AsyncTreeNode(attr));
36456     },
36457
36458     processResponse : function(response, node, callback)
36459     {
36460         var json = response.responseText;
36461         try {
36462             
36463             var o = Roo.decode(json);
36464             
36465             if (this.root === false && typeof(o.success) != undefined) {
36466                 this.root = 'data'; // the default behaviour for list like data..
36467                 }
36468                 
36469             if (this.root !== false &&  !o.success) {
36470                 // it's a failure condition.
36471                 var a = response.argument;
36472                 this.fireEvent("loadexception", this, a.node, response);
36473                 Roo.log("Load failed - should have a handler really");
36474                 return;
36475             }
36476             
36477             
36478             
36479             if (this.root !== false) {
36480                  o = o[this.root];
36481             }
36482             
36483             for(var i = 0, len = o.length; i < len; i++){
36484                 var n = this.createNode(o[i]);
36485                 if(n){
36486                     node.appendChild(n);
36487                 }
36488             }
36489             if(typeof callback == "function"){
36490                 callback(this, node);
36491             }
36492         }catch(e){
36493             this.handleFailure(response);
36494         }
36495     },
36496
36497     handleResponse : function(response){
36498         this.transId = false;
36499         var a = response.argument;
36500         this.processResponse(response, a.node, a.callback);
36501         this.fireEvent("load", this, a.node, response);
36502     },
36503
36504     handleFailure : function(response)
36505     {
36506         // should handle failure better..
36507         this.transId = false;
36508         var a = response.argument;
36509         this.fireEvent("loadexception", this, a.node, response);
36510         if(typeof a.callback == "function"){
36511             a.callback(this, a.node);
36512         }
36513     }
36514 });/*
36515  * Based on:
36516  * Ext JS Library 1.1.1
36517  * Copyright(c) 2006-2007, Ext JS, LLC.
36518  *
36519  * Originally Released Under LGPL - original licence link has changed is not relivant.
36520  *
36521  * Fork - LGPL
36522  * <script type="text/javascript">
36523  */
36524
36525 /**
36526 * @class Roo.tree.TreeFilter
36527 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36528 * @param {TreePanel} tree
36529 * @param {Object} config (optional)
36530  */
36531 Roo.tree.TreeFilter = function(tree, config){
36532     this.tree = tree;
36533     this.filtered = {};
36534     Roo.apply(this, config);
36535 };
36536
36537 Roo.tree.TreeFilter.prototype = {
36538     clearBlank:false,
36539     reverse:false,
36540     autoClear:false,
36541     remove:false,
36542
36543      /**
36544      * Filter the data by a specific attribute.
36545      * @param {String/RegExp} value Either string that the attribute value
36546      * should start with or a RegExp to test against the attribute
36547      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36548      * @param {TreeNode} startNode (optional) The node to start the filter at.
36549      */
36550     filter : function(value, attr, startNode){
36551         attr = attr || "text";
36552         var f;
36553         if(typeof value == "string"){
36554             var vlen = value.length;
36555             // auto clear empty filter
36556             if(vlen == 0 && this.clearBlank){
36557                 this.clear();
36558                 return;
36559             }
36560             value = value.toLowerCase();
36561             f = function(n){
36562                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36563             };
36564         }else if(value.exec){ // regex?
36565             f = function(n){
36566                 return value.test(n.attributes[attr]);
36567             };
36568         }else{
36569             throw 'Illegal filter type, must be string or regex';
36570         }
36571         this.filterBy(f, null, startNode);
36572         },
36573
36574     /**
36575      * Filter by a function. The passed function will be called with each
36576      * node in the tree (or from the startNode). If the function returns true, the node is kept
36577      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36578      * @param {Function} fn The filter function
36579      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36580      */
36581     filterBy : function(fn, scope, startNode){
36582         startNode = startNode || this.tree.root;
36583         if(this.autoClear){
36584             this.clear();
36585         }
36586         var af = this.filtered, rv = this.reverse;
36587         var f = function(n){
36588             if(n == startNode){
36589                 return true;
36590             }
36591             if(af[n.id]){
36592                 return false;
36593             }
36594             var m = fn.call(scope || n, n);
36595             if(!m || rv){
36596                 af[n.id] = n;
36597                 n.ui.hide();
36598                 return false;
36599             }
36600             return true;
36601         };
36602         startNode.cascade(f);
36603         if(this.remove){
36604            for(var id in af){
36605                if(typeof id != "function"){
36606                    var n = af[id];
36607                    if(n && n.parentNode){
36608                        n.parentNode.removeChild(n);
36609                    }
36610                }
36611            }
36612         }
36613     },
36614
36615     /**
36616      * Clears the current filter. Note: with the "remove" option
36617      * set a filter cannot be cleared.
36618      */
36619     clear : function(){
36620         var t = this.tree;
36621         var af = this.filtered;
36622         for(var id in af){
36623             if(typeof id != "function"){
36624                 var n = af[id];
36625                 if(n){
36626                     n.ui.show();
36627                 }
36628             }
36629         }
36630         this.filtered = {};
36631     }
36632 };
36633 /*
36634  * Based on:
36635  * Ext JS Library 1.1.1
36636  * Copyright(c) 2006-2007, Ext JS, LLC.
36637  *
36638  * Originally Released Under LGPL - original licence link has changed is not relivant.
36639  *
36640  * Fork - LGPL
36641  * <script type="text/javascript">
36642  */
36643  
36644
36645 /**
36646  * @class Roo.tree.TreeSorter
36647  * Provides sorting of nodes in a TreePanel
36648  * 
36649  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36650  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36651  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36652  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36653  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36654  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36655  * @constructor
36656  * @param {TreePanel} tree
36657  * @param {Object} config
36658  */
36659 Roo.tree.TreeSorter = function(tree, config){
36660     Roo.apply(this, config);
36661     tree.on("beforechildrenrendered", this.doSort, this);
36662     tree.on("append", this.updateSort, this);
36663     tree.on("insert", this.updateSort, this);
36664     
36665     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36666     var p = this.property || "text";
36667     var sortType = this.sortType;
36668     var fs = this.folderSort;
36669     var cs = this.caseSensitive === true;
36670     var leafAttr = this.leafAttr || 'leaf';
36671
36672     this.sortFn = function(n1, n2){
36673         if(fs){
36674             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36675                 return 1;
36676             }
36677             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36678                 return -1;
36679             }
36680         }
36681         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36682         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36683         if(v1 < v2){
36684                         return dsc ? +1 : -1;
36685                 }else if(v1 > v2){
36686                         return dsc ? -1 : +1;
36687         }else{
36688                 return 0;
36689         }
36690     };
36691 };
36692
36693 Roo.tree.TreeSorter.prototype = {
36694     doSort : function(node){
36695         node.sort(this.sortFn);
36696     },
36697     
36698     compareNodes : function(n1, n2){
36699         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36700     },
36701     
36702     updateSort : function(tree, node){
36703         if(node.childrenRendered){
36704             this.doSort.defer(1, this, [node]);
36705         }
36706     }
36707 };/*
36708  * Based on:
36709  * Ext JS Library 1.1.1
36710  * Copyright(c) 2006-2007, Ext JS, LLC.
36711  *
36712  * Originally Released Under LGPL - original licence link has changed is not relivant.
36713  *
36714  * Fork - LGPL
36715  * <script type="text/javascript">
36716  */
36717
36718 if(Roo.dd.DropZone){
36719     
36720 Roo.tree.TreeDropZone = function(tree, config){
36721     this.allowParentInsert = false;
36722     this.allowContainerDrop = false;
36723     this.appendOnly = false;
36724     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36725     this.tree = tree;
36726     this.lastInsertClass = "x-tree-no-status";
36727     this.dragOverData = {};
36728 };
36729
36730 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36731     ddGroup : "TreeDD",
36732     scroll:  true,
36733     
36734     expandDelay : 1000,
36735     
36736     expandNode : function(node){
36737         if(node.hasChildNodes() && !node.isExpanded()){
36738             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36739         }
36740     },
36741     
36742     queueExpand : function(node){
36743         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36744     },
36745     
36746     cancelExpand : function(){
36747         if(this.expandProcId){
36748             clearTimeout(this.expandProcId);
36749             this.expandProcId = false;
36750         }
36751     },
36752     
36753     isValidDropPoint : function(n, pt, dd, e, data){
36754         if(!n || !data){ return false; }
36755         var targetNode = n.node;
36756         var dropNode = data.node;
36757         // default drop rules
36758         if(!(targetNode && targetNode.isTarget && pt)){
36759             return false;
36760         }
36761         if(pt == "append" && targetNode.allowChildren === false){
36762             return false;
36763         }
36764         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36765             return false;
36766         }
36767         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36768             return false;
36769         }
36770         // reuse the object
36771         var overEvent = this.dragOverData;
36772         overEvent.tree = this.tree;
36773         overEvent.target = targetNode;
36774         overEvent.data = data;
36775         overEvent.point = pt;
36776         overEvent.source = dd;
36777         overEvent.rawEvent = e;
36778         overEvent.dropNode = dropNode;
36779         overEvent.cancel = false;  
36780         var result = this.tree.fireEvent("nodedragover", overEvent);
36781         return overEvent.cancel === false && result !== false;
36782     },
36783     
36784     getDropPoint : function(e, n, dd)
36785     {
36786         var tn = n.node;
36787         if(tn.isRoot){
36788             return tn.allowChildren !== false ? "append" : false; // always append for root
36789         }
36790         var dragEl = n.ddel;
36791         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36792         var y = Roo.lib.Event.getPageY(e);
36793         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36794         
36795         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36796         var noAppend = tn.allowChildren === false;
36797         if(this.appendOnly || tn.parentNode.allowChildren === false){
36798             return noAppend ? false : "append";
36799         }
36800         var noBelow = false;
36801         if(!this.allowParentInsert){
36802             noBelow = tn.hasChildNodes() && tn.isExpanded();
36803         }
36804         var q = (b - t) / (noAppend ? 2 : 3);
36805         if(y >= t && y < (t + q)){
36806             return "above";
36807         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36808             return "below";
36809         }else{
36810             return "append";
36811         }
36812     },
36813     
36814     onNodeEnter : function(n, dd, e, data)
36815     {
36816         this.cancelExpand();
36817     },
36818     
36819     onNodeOver : function(n, dd, e, data)
36820     {
36821        
36822         var pt = this.getDropPoint(e, n, dd);
36823         var node = n.node;
36824         
36825         // auto node expand check
36826         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36827             this.queueExpand(node);
36828         }else if(pt != "append"){
36829             this.cancelExpand();
36830         }
36831         
36832         // set the insert point style on the target node
36833         var returnCls = this.dropNotAllowed;
36834         if(this.isValidDropPoint(n, pt, dd, e, data)){
36835            if(pt){
36836                var el = n.ddel;
36837                var cls;
36838                if(pt == "above"){
36839                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36840                    cls = "x-tree-drag-insert-above";
36841                }else if(pt == "below"){
36842                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36843                    cls = "x-tree-drag-insert-below";
36844                }else{
36845                    returnCls = "x-tree-drop-ok-append";
36846                    cls = "x-tree-drag-append";
36847                }
36848                if(this.lastInsertClass != cls){
36849                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36850                    this.lastInsertClass = cls;
36851                }
36852            }
36853        }
36854        return returnCls;
36855     },
36856     
36857     onNodeOut : function(n, dd, e, data){
36858         
36859         this.cancelExpand();
36860         this.removeDropIndicators(n);
36861     },
36862     
36863     onNodeDrop : function(n, dd, e, data){
36864         var point = this.getDropPoint(e, n, dd);
36865         var targetNode = n.node;
36866         targetNode.ui.startDrop();
36867         if(!this.isValidDropPoint(n, point, dd, e, data)){
36868             targetNode.ui.endDrop();
36869             return false;
36870         }
36871         // first try to find the drop node
36872         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36873         var dropEvent = {
36874             tree : this.tree,
36875             target: targetNode,
36876             data: data,
36877             point: point,
36878             source: dd,
36879             rawEvent: e,
36880             dropNode: dropNode,
36881             cancel: !dropNode   
36882         };
36883         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36884         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36885             targetNode.ui.endDrop();
36886             return false;
36887         }
36888         // allow target changing
36889         targetNode = dropEvent.target;
36890         if(point == "append" && !targetNode.isExpanded()){
36891             targetNode.expand(false, null, function(){
36892                 this.completeDrop(dropEvent);
36893             }.createDelegate(this));
36894         }else{
36895             this.completeDrop(dropEvent);
36896         }
36897         return true;
36898     },
36899     
36900     completeDrop : function(de){
36901         var ns = de.dropNode, p = de.point, t = de.target;
36902         if(!(ns instanceof Array)){
36903             ns = [ns];
36904         }
36905         var n;
36906         for(var i = 0, len = ns.length; i < len; i++){
36907             n = ns[i];
36908             if(p == "above"){
36909                 t.parentNode.insertBefore(n, t);
36910             }else if(p == "below"){
36911                 t.parentNode.insertBefore(n, t.nextSibling);
36912             }else{
36913                 t.appendChild(n);
36914             }
36915         }
36916         n.ui.focus();
36917         if(this.tree.hlDrop){
36918             n.ui.highlight();
36919         }
36920         t.ui.endDrop();
36921         this.tree.fireEvent("nodedrop", de);
36922     },
36923     
36924     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36925         if(this.tree.hlDrop){
36926             dropNode.ui.focus();
36927             dropNode.ui.highlight();
36928         }
36929         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36930     },
36931     
36932     getTree : function(){
36933         return this.tree;
36934     },
36935     
36936     removeDropIndicators : function(n){
36937         if(n && n.ddel){
36938             var el = n.ddel;
36939             Roo.fly(el).removeClass([
36940                     "x-tree-drag-insert-above",
36941                     "x-tree-drag-insert-below",
36942                     "x-tree-drag-append"]);
36943             this.lastInsertClass = "_noclass";
36944         }
36945     },
36946     
36947     beforeDragDrop : function(target, e, id){
36948         this.cancelExpand();
36949         return true;
36950     },
36951     
36952     afterRepair : function(data){
36953         if(data && Roo.enableFx){
36954             data.node.ui.highlight();
36955         }
36956         this.hideProxy();
36957     } 
36958     
36959 });
36960
36961 }
36962 /*
36963  * Based on:
36964  * Ext JS Library 1.1.1
36965  * Copyright(c) 2006-2007, Ext JS, LLC.
36966  *
36967  * Originally Released Under LGPL - original licence link has changed is not relivant.
36968  *
36969  * Fork - LGPL
36970  * <script type="text/javascript">
36971  */
36972  
36973
36974 if(Roo.dd.DragZone){
36975 Roo.tree.TreeDragZone = function(tree, config){
36976     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36977     this.tree = tree;
36978 };
36979
36980 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36981     ddGroup : "TreeDD",
36982    
36983     onBeforeDrag : function(data, e){
36984         var n = data.node;
36985         return n && n.draggable && !n.disabled;
36986     },
36987      
36988     
36989     onInitDrag : function(e){
36990         var data = this.dragData;
36991         this.tree.getSelectionModel().select(data.node);
36992         this.proxy.update("");
36993         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36994         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36995     },
36996     
36997     getRepairXY : function(e, data){
36998         return data.node.ui.getDDRepairXY();
36999     },
37000     
37001     onEndDrag : function(data, e){
37002         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37003         
37004         
37005     },
37006     
37007     onValidDrop : function(dd, e, id){
37008         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37009         this.hideProxy();
37010     },
37011     
37012     beforeInvalidDrop : function(e, id){
37013         // this scrolls the original position back into view
37014         var sm = this.tree.getSelectionModel();
37015         sm.clearSelections();
37016         sm.select(this.dragData.node);
37017     }
37018 });
37019 }/*
37020  * Based on:
37021  * Ext JS Library 1.1.1
37022  * Copyright(c) 2006-2007, Ext JS, LLC.
37023  *
37024  * Originally Released Under LGPL - original licence link has changed is not relivant.
37025  *
37026  * Fork - LGPL
37027  * <script type="text/javascript">
37028  */
37029 /**
37030  * @class Roo.tree.TreeEditor
37031  * @extends Roo.Editor
37032  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37033  * as the editor field.
37034  * @constructor
37035  * @param {Object} config (used to be the tree panel.)
37036  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37037  * 
37038  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37039  * @cfg {Roo.form.TextField|Object} field The field configuration
37040  *
37041  * 
37042  */
37043 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37044     var tree = config;
37045     var field;
37046     if (oldconfig) { // old style..
37047         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37048     } else {
37049         // new style..
37050         tree = config.tree;
37051         config.field = config.field  || {};
37052         config.field.xtype = 'TextField';
37053         field = Roo.factory(config.field, Roo.form);
37054     }
37055     config = config || {};
37056     
37057     
37058     this.addEvents({
37059         /**
37060          * @event beforenodeedit
37061          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37062          * false from the handler of this event.
37063          * @param {Editor} this
37064          * @param {Roo.tree.Node} node 
37065          */
37066         "beforenodeedit" : true
37067     });
37068     
37069     //Roo.log(config);
37070     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37071
37072     this.tree = tree;
37073
37074     tree.on('beforeclick', this.beforeNodeClick, this);
37075     tree.getTreeEl().on('mousedown', this.hide, this);
37076     this.on('complete', this.updateNode, this);
37077     this.on('beforestartedit', this.fitToTree, this);
37078     this.on('startedit', this.bindScroll, this, {delay:10});
37079     this.on('specialkey', this.onSpecialKey, this);
37080 };
37081
37082 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
37083     /**
37084      * @cfg {String} alignment
37085      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
37086      */
37087     alignment: "l-l",
37088     // inherit
37089     autoSize: false,
37090     /**
37091      * @cfg {Boolean} hideEl
37092      * True to hide the bound element while the editor is displayed (defaults to false)
37093      */
37094     hideEl : false,
37095     /**
37096      * @cfg {String} cls
37097      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
37098      */
37099     cls: "x-small-editor x-tree-editor",
37100     /**
37101      * @cfg {Boolean} shim
37102      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
37103      */
37104     shim:false,
37105     // inherit
37106     shadow:"frame",
37107     /**
37108      * @cfg {Number} maxWidth
37109      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
37110      * the containing tree element's size, it will be automatically limited for you to the container width, taking
37111      * scroll and client offsets into account prior to each edit.
37112      */
37113     maxWidth: 250,
37114
37115     editDelay : 350,
37116
37117     // private
37118     fitToTree : function(ed, el){
37119         var td = this.tree.getTreeEl().dom, nd = el.dom;
37120         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
37121             td.scrollLeft = nd.offsetLeft;
37122         }
37123         var w = Math.min(
37124                 this.maxWidth,
37125                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
37126         this.setSize(w, '');
37127         
37128         return this.fireEvent('beforenodeedit', this, this.editNode);
37129         
37130     },
37131
37132     // private
37133     triggerEdit : function(node){
37134         this.completeEdit();
37135         this.editNode = node;
37136         this.startEdit(node.ui.textNode, node.text);
37137     },
37138
37139     // private
37140     bindScroll : function(){
37141         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37142     },
37143
37144     // private
37145     beforeNodeClick : function(node, e){
37146         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37147         this.lastClick = new Date();
37148         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37149             e.stopEvent();
37150             this.triggerEdit(node);
37151             return false;
37152         }
37153         return true;
37154     },
37155
37156     // private
37157     updateNode : function(ed, value){
37158         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
37159         this.editNode.setText(value);
37160     },
37161
37162     // private
37163     onHide : function(){
37164         Roo.tree.TreeEditor.superclass.onHide.call(this);
37165         if(this.editNode){
37166             this.editNode.ui.focus();
37167         }
37168     },
37169
37170     // private
37171     onSpecialKey : function(field, e){
37172         var k = e.getKey();
37173         if(k == e.ESC){
37174             e.stopEvent();
37175             this.cancelEdit();
37176         }else if(k == e.ENTER && !e.hasModifier()){
37177             e.stopEvent();
37178             this.completeEdit();
37179         }
37180     }
37181 });//<Script type="text/javascript">
37182 /*
37183  * Based on:
37184  * Ext JS Library 1.1.1
37185  * Copyright(c) 2006-2007, Ext JS, LLC.
37186  *
37187  * Originally Released Under LGPL - original licence link has changed is not relivant.
37188  *
37189  * Fork - LGPL
37190  * <script type="text/javascript">
37191  */
37192  
37193 /**
37194  * Not documented??? - probably should be...
37195  */
37196
37197 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
37198     //focus: Roo.emptyFn, // prevent odd scrolling behavior
37199     
37200     renderElements : function(n, a, targetNode, bulkRender){
37201         //consel.log("renderElements?");
37202         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37203
37204         var t = n.getOwnerTree();
37205         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37206         
37207         var cols = t.columns;
37208         var bw = t.borderWidth;
37209         var c = cols[0];
37210         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37211          var cb = typeof a.checked == "boolean";
37212         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37213         var colcls = 'x-t-' + tid + '-c0';
37214         var buf = [
37215             '<li class="x-tree-node">',
37216             
37217                 
37218                 '<div class="x-tree-node-el ', a.cls,'">',
37219                     // extran...
37220                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37221                 
37222                 
37223                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37224                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37225                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37226                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37227                            (a.iconCls ? ' '+a.iconCls : ''),
37228                            '" unselectable="on" />',
37229                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37230                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37231                              
37232                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37233                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37234                             '<span unselectable="on" qtip="' + tx + '">',
37235                              tx,
37236                              '</span></a>' ,
37237                     '</div>',
37238                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37239                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37240                  ];
37241         for(var i = 1, len = cols.length; i < len; i++){
37242             c = cols[i];
37243             colcls = 'x-t-' + tid + '-c' +i;
37244             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37245             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37246                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37247                       "</div>");
37248          }
37249          
37250          buf.push(
37251             '</a>',
37252             '<div class="x-clear"></div></div>',
37253             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37254             "</li>");
37255         
37256         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37257             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37258                                 n.nextSibling.ui.getEl(), buf.join(""));
37259         }else{
37260             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37261         }
37262         var el = this.wrap.firstChild;
37263         this.elRow = el;
37264         this.elNode = el.firstChild;
37265         this.ranchor = el.childNodes[1];
37266         this.ctNode = this.wrap.childNodes[1];
37267         var cs = el.firstChild.childNodes;
37268         this.indentNode = cs[0];
37269         this.ecNode = cs[1];
37270         this.iconNode = cs[2];
37271         var index = 3;
37272         if(cb){
37273             this.checkbox = cs[3];
37274             index++;
37275         }
37276         this.anchor = cs[index];
37277         
37278         this.textNode = cs[index].firstChild;
37279         
37280         //el.on("click", this.onClick, this);
37281         //el.on("dblclick", this.onDblClick, this);
37282         
37283         
37284        // console.log(this);
37285     },
37286     initEvents : function(){
37287         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37288         
37289             
37290         var a = this.ranchor;
37291
37292         var el = Roo.get(a);
37293
37294         if(Roo.isOpera){ // opera render bug ignores the CSS
37295             el.setStyle("text-decoration", "none");
37296         }
37297
37298         el.on("click", this.onClick, this);
37299         el.on("dblclick", this.onDblClick, this);
37300         el.on("contextmenu", this.onContextMenu, this);
37301         
37302     },
37303     
37304     /*onSelectedChange : function(state){
37305         if(state){
37306             this.focus();
37307             this.addClass("x-tree-selected");
37308         }else{
37309             //this.blur();
37310             this.removeClass("x-tree-selected");
37311         }
37312     },*/
37313     addClass : function(cls){
37314         if(this.elRow){
37315             Roo.fly(this.elRow).addClass(cls);
37316         }
37317         
37318     },
37319     
37320     
37321     removeClass : function(cls){
37322         if(this.elRow){
37323             Roo.fly(this.elRow).removeClass(cls);
37324         }
37325     }
37326
37327     
37328     
37329 });//<Script type="text/javascript">
37330
37331 /*
37332  * Based on:
37333  * Ext JS Library 1.1.1
37334  * Copyright(c) 2006-2007, Ext JS, LLC.
37335  *
37336  * Originally Released Under LGPL - original licence link has changed is not relivant.
37337  *
37338  * Fork - LGPL
37339  * <script type="text/javascript">
37340  */
37341  
37342
37343 /**
37344  * @class Roo.tree.ColumnTree
37345  * @extends Roo.data.TreePanel
37346  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37347  * @cfg {int} borderWidth  compined right/left border allowance
37348  * @constructor
37349  * @param {String/HTMLElement/Element} el The container element
37350  * @param {Object} config
37351  */
37352 Roo.tree.ColumnTree =  function(el, config)
37353 {
37354    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37355    this.addEvents({
37356         /**
37357         * @event resize
37358         * Fire this event on a container when it resizes
37359         * @param {int} w Width
37360         * @param {int} h Height
37361         */
37362        "resize" : true
37363     });
37364     this.on('resize', this.onResize, this);
37365 };
37366
37367 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37368     //lines:false,
37369     
37370     
37371     borderWidth: Roo.isBorderBox ? 0 : 2, 
37372     headEls : false,
37373     
37374     render : function(){
37375         // add the header.....
37376        
37377         Roo.tree.ColumnTree.superclass.render.apply(this);
37378         
37379         this.el.addClass('x-column-tree');
37380         
37381         this.headers = this.el.createChild(
37382             {cls:'x-tree-headers'},this.innerCt.dom);
37383    
37384         var cols = this.columns, c;
37385         var totalWidth = 0;
37386         this.headEls = [];
37387         var  len = cols.length;
37388         for(var i = 0; i < len; i++){
37389              c = cols[i];
37390              totalWidth += c.width;
37391             this.headEls.push(this.headers.createChild({
37392                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37393                  cn: {
37394                      cls:'x-tree-hd-text',
37395                      html: c.header
37396                  },
37397                  style:'width:'+(c.width-this.borderWidth)+'px;'
37398              }));
37399         }
37400         this.headers.createChild({cls:'x-clear'});
37401         // prevent floats from wrapping when clipped
37402         this.headers.setWidth(totalWidth);
37403         //this.innerCt.setWidth(totalWidth);
37404         this.innerCt.setStyle({ overflow: 'auto' });
37405         this.onResize(this.width, this.height);
37406              
37407         
37408     },
37409     onResize : function(w,h)
37410     {
37411         this.height = h;
37412         this.width = w;
37413         // resize cols..
37414         this.innerCt.setWidth(this.width);
37415         this.innerCt.setHeight(this.height-20);
37416         
37417         // headers...
37418         var cols = this.columns, c;
37419         var totalWidth = 0;
37420         var expEl = false;
37421         var len = cols.length;
37422         for(var i = 0; i < len; i++){
37423             c = cols[i];
37424             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37425                 // it's the expander..
37426                 expEl  = this.headEls[i];
37427                 continue;
37428             }
37429             totalWidth += c.width;
37430             
37431         }
37432         if (expEl) {
37433             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37434         }
37435         this.headers.setWidth(w-20);
37436
37437         
37438         
37439         
37440     }
37441 });
37442 /*
37443  * Based on:
37444  * Ext JS Library 1.1.1
37445  * Copyright(c) 2006-2007, Ext JS, LLC.
37446  *
37447  * Originally Released Under LGPL - original licence link has changed is not relivant.
37448  *
37449  * Fork - LGPL
37450  * <script type="text/javascript">
37451  */
37452  
37453 /**
37454  * @class Roo.menu.Menu
37455  * @extends Roo.util.Observable
37456  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37457  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37458  * @constructor
37459  * Creates a new Menu
37460  * @param {Object} config Configuration options
37461  */
37462 Roo.menu.Menu = function(config){
37463     
37464     Roo.menu.Menu.superclass.constructor.call(this, config);
37465     
37466     this.id = this.id || Roo.id();
37467     this.addEvents({
37468         /**
37469          * @event beforeshow
37470          * Fires before this menu is displayed
37471          * @param {Roo.menu.Menu} this
37472          */
37473         beforeshow : true,
37474         /**
37475          * @event beforehide
37476          * Fires before this menu is hidden
37477          * @param {Roo.menu.Menu} this
37478          */
37479         beforehide : true,
37480         /**
37481          * @event show
37482          * Fires after this menu is displayed
37483          * @param {Roo.menu.Menu} this
37484          */
37485         show : true,
37486         /**
37487          * @event hide
37488          * Fires after this menu is hidden
37489          * @param {Roo.menu.Menu} this
37490          */
37491         hide : true,
37492         /**
37493          * @event click
37494          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37495          * @param {Roo.menu.Menu} this
37496          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37497          * @param {Roo.EventObject} e
37498          */
37499         click : true,
37500         /**
37501          * @event mouseover
37502          * Fires when the mouse is hovering over this menu
37503          * @param {Roo.menu.Menu} this
37504          * @param {Roo.EventObject} e
37505          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37506          */
37507         mouseover : true,
37508         /**
37509          * @event mouseout
37510          * Fires when the mouse exits this menu
37511          * @param {Roo.menu.Menu} this
37512          * @param {Roo.EventObject} e
37513          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37514          */
37515         mouseout : true,
37516         /**
37517          * @event itemclick
37518          * Fires when a menu item contained in this menu is clicked
37519          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37520          * @param {Roo.EventObject} e
37521          */
37522         itemclick: true
37523     });
37524     if (this.registerMenu) {
37525         Roo.menu.MenuMgr.register(this);
37526     }
37527     
37528     var mis = this.items;
37529     this.items = new Roo.util.MixedCollection();
37530     if(mis){
37531         this.add.apply(this, mis);
37532     }
37533 };
37534
37535 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37536     /**
37537      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37538      */
37539     minWidth : 120,
37540     /**
37541      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37542      * for bottom-right shadow (defaults to "sides")
37543      */
37544     shadow : "sides",
37545     /**
37546      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37547      * this menu (defaults to "tl-tr?")
37548      */
37549     subMenuAlign : "tl-tr?",
37550     /**
37551      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37552      * relative to its element of origin (defaults to "tl-bl?")
37553      */
37554     defaultAlign : "tl-bl?",
37555     /**
37556      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37557      */
37558     allowOtherMenus : false,
37559     /**
37560      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37561      */
37562     registerMenu : true,
37563
37564     hidden:true,
37565
37566     // private
37567     render : function(){
37568         if(this.el){
37569             return;
37570         }
37571         var el = this.el = new Roo.Layer({
37572             cls: "x-menu",
37573             shadow:this.shadow,
37574             constrain: false,
37575             parentEl: this.parentEl || document.body,
37576             zindex:15000
37577         });
37578
37579         this.keyNav = new Roo.menu.MenuNav(this);
37580
37581         if(this.plain){
37582             el.addClass("x-menu-plain");
37583         }
37584         if(this.cls){
37585             el.addClass(this.cls);
37586         }
37587         // generic focus element
37588         this.focusEl = el.createChild({
37589             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37590         });
37591         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37592         //disabling touch- as it's causing issues ..
37593         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37594         ul.on('click'   , this.onClick, this);
37595         
37596         
37597         ul.on("mouseover", this.onMouseOver, this);
37598         ul.on("mouseout", this.onMouseOut, this);
37599         this.items.each(function(item){
37600             if (item.hidden) {
37601                 return;
37602             }
37603             
37604             var li = document.createElement("li");
37605             li.className = "x-menu-list-item";
37606             ul.dom.appendChild(li);
37607             item.render(li, this);
37608         }, this);
37609         this.ul = ul;
37610         this.autoWidth();
37611     },
37612
37613     // private
37614     autoWidth : function(){
37615         var el = this.el, ul = this.ul;
37616         if(!el){
37617             return;
37618         }
37619         var w = this.width;
37620         if(w){
37621             el.setWidth(w);
37622         }else if(Roo.isIE){
37623             el.setWidth(this.minWidth);
37624             var t = el.dom.offsetWidth; // force recalc
37625             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37626         }
37627     },
37628
37629     // private
37630     delayAutoWidth : function(){
37631         if(this.rendered){
37632             if(!this.awTask){
37633                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37634             }
37635             this.awTask.delay(20);
37636         }
37637     },
37638
37639     // private
37640     findTargetItem : function(e){
37641         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37642         if(t && t.menuItemId){
37643             return this.items.get(t.menuItemId);
37644         }
37645     },
37646
37647     // private
37648     onClick : function(e){
37649         Roo.log("menu.onClick");
37650         var t = this.findTargetItem(e);
37651         if(!t){
37652             return;
37653         }
37654         Roo.log(e);
37655         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37656             if(t == this.activeItem && t.shouldDeactivate(e)){
37657                 this.activeItem.deactivate();
37658                 delete this.activeItem;
37659                 return;
37660             }
37661             if(t.canActivate){
37662                 this.setActiveItem(t, true);
37663             }
37664             return;
37665             
37666             
37667         }
37668         
37669         t.onClick(e);
37670         this.fireEvent("click", this, t, e);
37671     },
37672
37673     // private
37674     setActiveItem : function(item, autoExpand){
37675         if(item != this.activeItem){
37676             if(this.activeItem){
37677                 this.activeItem.deactivate();
37678             }
37679             this.activeItem = item;
37680             item.activate(autoExpand);
37681         }else if(autoExpand){
37682             item.expandMenu();
37683         }
37684     },
37685
37686     // private
37687     tryActivate : function(start, step){
37688         var items = this.items;
37689         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37690             var item = items.get(i);
37691             if(!item.disabled && item.canActivate){
37692                 this.setActiveItem(item, false);
37693                 return item;
37694             }
37695         }
37696         return false;
37697     },
37698
37699     // private
37700     onMouseOver : function(e){
37701         var t;
37702         if(t = this.findTargetItem(e)){
37703             if(t.canActivate && !t.disabled){
37704                 this.setActiveItem(t, true);
37705             }
37706         }
37707         this.fireEvent("mouseover", this, e, t);
37708     },
37709
37710     // private
37711     onMouseOut : function(e){
37712         var t;
37713         if(t = this.findTargetItem(e)){
37714             if(t == this.activeItem && t.shouldDeactivate(e)){
37715                 this.activeItem.deactivate();
37716                 delete this.activeItem;
37717             }
37718         }
37719         this.fireEvent("mouseout", this, e, t);
37720     },
37721
37722     /**
37723      * Read-only.  Returns true if the menu is currently displayed, else false.
37724      * @type Boolean
37725      */
37726     isVisible : function(){
37727         return this.el && !this.hidden;
37728     },
37729
37730     /**
37731      * Displays this menu relative to another element
37732      * @param {String/HTMLElement/Roo.Element} element The element to align to
37733      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37734      * the element (defaults to this.defaultAlign)
37735      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37736      */
37737     show : function(el, pos, parentMenu){
37738         this.parentMenu = parentMenu;
37739         if(!this.el){
37740             this.render();
37741         }
37742         this.fireEvent("beforeshow", this);
37743         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37744     },
37745
37746     /**
37747      * Displays this menu at a specific xy position
37748      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37749      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37750      */
37751     showAt : function(xy, parentMenu, /* private: */_e){
37752         this.parentMenu = parentMenu;
37753         if(!this.el){
37754             this.render();
37755         }
37756         if(_e !== false){
37757             this.fireEvent("beforeshow", this);
37758             xy = this.el.adjustForConstraints(xy);
37759         }
37760         this.el.setXY(xy);
37761         this.el.show();
37762         this.hidden = false;
37763         this.focus();
37764         this.fireEvent("show", this);
37765     },
37766
37767     focus : function(){
37768         if(!this.hidden){
37769             this.doFocus.defer(50, this);
37770         }
37771     },
37772
37773     doFocus : function(){
37774         if(!this.hidden){
37775             this.focusEl.focus();
37776         }
37777     },
37778
37779     /**
37780      * Hides this menu and optionally all parent menus
37781      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37782      */
37783     hide : function(deep){
37784         if(this.el && this.isVisible()){
37785             this.fireEvent("beforehide", this);
37786             if(this.activeItem){
37787                 this.activeItem.deactivate();
37788                 this.activeItem = null;
37789             }
37790             this.el.hide();
37791             this.hidden = true;
37792             this.fireEvent("hide", this);
37793         }
37794         if(deep === true && this.parentMenu){
37795             this.parentMenu.hide(true);
37796         }
37797     },
37798
37799     /**
37800      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37801      * Any of the following are valid:
37802      * <ul>
37803      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37804      * <li>An HTMLElement object which will be converted to a menu item</li>
37805      * <li>A menu item config object that will be created as a new menu item</li>
37806      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37807      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37808      * </ul>
37809      * Usage:
37810      * <pre><code>
37811 // Create the menu
37812 var menu = new Roo.menu.Menu();
37813
37814 // Create a menu item to add by reference
37815 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37816
37817 // Add a bunch of items at once using different methods.
37818 // Only the last item added will be returned.
37819 var item = menu.add(
37820     menuItem,                // add existing item by ref
37821     'Dynamic Item',          // new TextItem
37822     '-',                     // new separator
37823     { text: 'Config Item' }  // new item by config
37824 );
37825 </code></pre>
37826      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37827      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37828      */
37829     add : function(){
37830         var a = arguments, l = a.length, item;
37831         for(var i = 0; i < l; i++){
37832             var el = a[i];
37833             if ((typeof(el) == "object") && el.xtype && el.xns) {
37834                 el = Roo.factory(el, Roo.menu);
37835             }
37836             
37837             if(el.render){ // some kind of Item
37838                 item = this.addItem(el);
37839             }else if(typeof el == "string"){ // string
37840                 if(el == "separator" || el == "-"){
37841                     item = this.addSeparator();
37842                 }else{
37843                     item = this.addText(el);
37844                 }
37845             }else if(el.tagName || el.el){ // element
37846                 item = this.addElement(el);
37847             }else if(typeof el == "object"){ // must be menu item config?
37848                 item = this.addMenuItem(el);
37849             }
37850         }
37851         return item;
37852     },
37853
37854     /**
37855      * Returns this menu's underlying {@link Roo.Element} object
37856      * @return {Roo.Element} The element
37857      */
37858     getEl : function(){
37859         if(!this.el){
37860             this.render();
37861         }
37862         return this.el;
37863     },
37864
37865     /**
37866      * Adds a separator bar to the menu
37867      * @return {Roo.menu.Item} The menu item that was added
37868      */
37869     addSeparator : function(){
37870         return this.addItem(new Roo.menu.Separator());
37871     },
37872
37873     /**
37874      * Adds an {@link Roo.Element} object to the menu
37875      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37876      * @return {Roo.menu.Item} The menu item that was added
37877      */
37878     addElement : function(el){
37879         return this.addItem(new Roo.menu.BaseItem(el));
37880     },
37881
37882     /**
37883      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37884      * @param {Roo.menu.Item} item The menu item to add
37885      * @return {Roo.menu.Item} The menu item that was added
37886      */
37887     addItem : function(item){
37888         this.items.add(item);
37889         if(this.ul){
37890             var li = document.createElement("li");
37891             li.className = "x-menu-list-item";
37892             this.ul.dom.appendChild(li);
37893             item.render(li, this);
37894             this.delayAutoWidth();
37895         }
37896         return item;
37897     },
37898
37899     /**
37900      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37901      * @param {Object} config A MenuItem config object
37902      * @return {Roo.menu.Item} The menu item that was added
37903      */
37904     addMenuItem : function(config){
37905         if(!(config instanceof Roo.menu.Item)){
37906             if(typeof config.checked == "boolean"){ // must be check menu item config?
37907                 config = new Roo.menu.CheckItem(config);
37908             }else{
37909                 config = new Roo.menu.Item(config);
37910             }
37911         }
37912         return this.addItem(config);
37913     },
37914
37915     /**
37916      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37917      * @param {String} text The text to display in the menu item
37918      * @return {Roo.menu.Item} The menu item that was added
37919      */
37920     addText : function(text){
37921         return this.addItem(new Roo.menu.TextItem({ text : text }));
37922     },
37923
37924     /**
37925      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37926      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37927      * @param {Roo.menu.Item} item The menu item to add
37928      * @return {Roo.menu.Item} The menu item that was added
37929      */
37930     insert : function(index, item){
37931         this.items.insert(index, item);
37932         if(this.ul){
37933             var li = document.createElement("li");
37934             li.className = "x-menu-list-item";
37935             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37936             item.render(li, this);
37937             this.delayAutoWidth();
37938         }
37939         return item;
37940     },
37941
37942     /**
37943      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37944      * @param {Roo.menu.Item} item The menu item to remove
37945      */
37946     remove : function(item){
37947         this.items.removeKey(item.id);
37948         item.destroy();
37949     },
37950
37951     /**
37952      * Removes and destroys all items in the menu
37953      */
37954     removeAll : function(){
37955         var f;
37956         while(f = this.items.first()){
37957             this.remove(f);
37958         }
37959     }
37960 });
37961
37962 // MenuNav is a private utility class used internally by the Menu
37963 Roo.menu.MenuNav = function(menu){
37964     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37965     this.scope = this.menu = menu;
37966 };
37967
37968 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37969     doRelay : function(e, h){
37970         var k = e.getKey();
37971         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37972             this.menu.tryActivate(0, 1);
37973             return false;
37974         }
37975         return h.call(this.scope || this, e, this.menu);
37976     },
37977
37978     up : function(e, m){
37979         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37980             m.tryActivate(m.items.length-1, -1);
37981         }
37982     },
37983
37984     down : function(e, m){
37985         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37986             m.tryActivate(0, 1);
37987         }
37988     },
37989
37990     right : function(e, m){
37991         if(m.activeItem){
37992             m.activeItem.expandMenu(true);
37993         }
37994     },
37995
37996     left : function(e, m){
37997         m.hide();
37998         if(m.parentMenu && m.parentMenu.activeItem){
37999             m.parentMenu.activeItem.activate();
38000         }
38001     },
38002
38003     enter : function(e, m){
38004         if(m.activeItem){
38005             e.stopPropagation();
38006             m.activeItem.onClick(e);
38007             m.fireEvent("click", this, m.activeItem);
38008             return true;
38009         }
38010     }
38011 });/*
38012  * Based on:
38013  * Ext JS Library 1.1.1
38014  * Copyright(c) 2006-2007, Ext JS, LLC.
38015  *
38016  * Originally Released Under LGPL - original licence link has changed is not relivant.
38017  *
38018  * Fork - LGPL
38019  * <script type="text/javascript">
38020  */
38021  
38022 /**
38023  * @class Roo.menu.MenuMgr
38024  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38025  * @singleton
38026  */
38027 Roo.menu.MenuMgr = function(){
38028    var menus, active, groups = {}, attached = false, lastShow = new Date();
38029
38030    // private - called when first menu is created
38031    function init(){
38032        menus = {};
38033        active = new Roo.util.MixedCollection();
38034        Roo.get(document).addKeyListener(27, function(){
38035            if(active.length > 0){
38036                hideAll();
38037            }
38038        });
38039    }
38040
38041    // private
38042    function hideAll(){
38043        if(active && active.length > 0){
38044            var c = active.clone();
38045            c.each(function(m){
38046                m.hide();
38047            });
38048        }
38049    }
38050
38051    // private
38052    function onHide(m){
38053        active.remove(m);
38054        if(active.length < 1){
38055            Roo.get(document).un("mousedown", onMouseDown);
38056            attached = false;
38057        }
38058    }
38059
38060    // private
38061    function onShow(m){
38062        var last = active.last();
38063        lastShow = new Date();
38064        active.add(m);
38065        if(!attached){
38066            Roo.get(document).on("mousedown", onMouseDown);
38067            attached = true;
38068        }
38069        if(m.parentMenu){
38070           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38071           m.parentMenu.activeChild = m;
38072        }else if(last && last.isVisible()){
38073           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38074        }
38075    }
38076
38077    // private
38078    function onBeforeHide(m){
38079        if(m.activeChild){
38080            m.activeChild.hide();
38081        }
38082        if(m.autoHideTimer){
38083            clearTimeout(m.autoHideTimer);
38084            delete m.autoHideTimer;
38085        }
38086    }
38087
38088    // private
38089    function onBeforeShow(m){
38090        var pm = m.parentMenu;
38091        if(!pm && !m.allowOtherMenus){
38092            hideAll();
38093        }else if(pm && pm.activeChild && active != m){
38094            pm.activeChild.hide();
38095        }
38096    }
38097
38098    // private
38099    function onMouseDown(e){
38100        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
38101            hideAll();
38102        }
38103    }
38104
38105    // private
38106    function onBeforeCheck(mi, state){
38107        if(state){
38108            var g = groups[mi.group];
38109            for(var i = 0, l = g.length; i < l; i++){
38110                if(g[i] != mi){
38111                    g[i].setChecked(false);
38112                }
38113            }
38114        }
38115    }
38116
38117    return {
38118
38119        /**
38120         * Hides all menus that are currently visible
38121         */
38122        hideAll : function(){
38123             hideAll();  
38124        },
38125
38126        // private
38127        register : function(menu){
38128            if(!menus){
38129                init();
38130            }
38131            menus[menu.id] = menu;
38132            menu.on("beforehide", onBeforeHide);
38133            menu.on("hide", onHide);
38134            menu.on("beforeshow", onBeforeShow);
38135            menu.on("show", onShow);
38136            var g = menu.group;
38137            if(g && menu.events["checkchange"]){
38138                if(!groups[g]){
38139                    groups[g] = [];
38140                }
38141                groups[g].push(menu);
38142                menu.on("checkchange", onCheck);
38143            }
38144        },
38145
38146         /**
38147          * Returns a {@link Roo.menu.Menu} object
38148          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38149          * be used to generate and return a new Menu instance.
38150          */
38151        get : function(menu){
38152            if(typeof menu == "string"){ // menu id
38153                return menus[menu];
38154            }else if(menu.events){  // menu instance
38155                return menu;
38156            }else if(typeof menu.length == 'number'){ // array of menu items?
38157                return new Roo.menu.Menu({items:menu});
38158            }else{ // otherwise, must be a config
38159                return new Roo.menu.Menu(menu);
38160            }
38161        },
38162
38163        // private
38164        unregister : function(menu){
38165            delete menus[menu.id];
38166            menu.un("beforehide", onBeforeHide);
38167            menu.un("hide", onHide);
38168            menu.un("beforeshow", onBeforeShow);
38169            menu.un("show", onShow);
38170            var g = menu.group;
38171            if(g && menu.events["checkchange"]){
38172                groups[g].remove(menu);
38173                menu.un("checkchange", onCheck);
38174            }
38175        },
38176
38177        // private
38178        registerCheckable : function(menuItem){
38179            var g = menuItem.group;
38180            if(g){
38181                if(!groups[g]){
38182                    groups[g] = [];
38183                }
38184                groups[g].push(menuItem);
38185                menuItem.on("beforecheckchange", onBeforeCheck);
38186            }
38187        },
38188
38189        // private
38190        unregisterCheckable : function(menuItem){
38191            var g = menuItem.group;
38192            if(g){
38193                groups[g].remove(menuItem);
38194                menuItem.un("beforecheckchange", onBeforeCheck);
38195            }
38196        }
38197    };
38198 }();/*
38199  * Based on:
38200  * Ext JS Library 1.1.1
38201  * Copyright(c) 2006-2007, Ext JS, LLC.
38202  *
38203  * Originally Released Under LGPL - original licence link has changed is not relivant.
38204  *
38205  * Fork - LGPL
38206  * <script type="text/javascript">
38207  */
38208  
38209
38210 /**
38211  * @class Roo.menu.BaseItem
38212  * @extends Roo.Component
38213  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38214  * management and base configuration options shared by all menu components.
38215  * @constructor
38216  * Creates a new BaseItem
38217  * @param {Object} config Configuration options
38218  */
38219 Roo.menu.BaseItem = function(config){
38220     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38221
38222     this.addEvents({
38223         /**
38224          * @event click
38225          * Fires when this item is clicked
38226          * @param {Roo.menu.BaseItem} this
38227          * @param {Roo.EventObject} e
38228          */
38229         click: true,
38230         /**
38231          * @event activate
38232          * Fires when this item is activated
38233          * @param {Roo.menu.BaseItem} this
38234          */
38235         activate : true,
38236         /**
38237          * @event deactivate
38238          * Fires when this item is deactivated
38239          * @param {Roo.menu.BaseItem} this
38240          */
38241         deactivate : true
38242     });
38243
38244     if(this.handler){
38245         this.on("click", this.handler, this.scope, true);
38246     }
38247 };
38248
38249 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38250     /**
38251      * @cfg {Function} handler
38252      * A function that will handle the click event of this menu item (defaults to undefined)
38253      */
38254     /**
38255      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38256      */
38257     canActivate : false,
38258     
38259      /**
38260      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38261      */
38262     hidden: false,
38263     
38264     /**
38265      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38266      */
38267     activeClass : "x-menu-item-active",
38268     /**
38269      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38270      */
38271     hideOnClick : true,
38272     /**
38273      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38274      */
38275     hideDelay : 100,
38276
38277     // private
38278     ctype: "Roo.menu.BaseItem",
38279
38280     // private
38281     actionMode : "container",
38282
38283     // private
38284     render : function(container, parentMenu){
38285         this.parentMenu = parentMenu;
38286         Roo.menu.BaseItem.superclass.render.call(this, container);
38287         this.container.menuItemId = this.id;
38288     },
38289
38290     // private
38291     onRender : function(container, position){
38292         this.el = Roo.get(this.el);
38293         container.dom.appendChild(this.el.dom);
38294     },
38295
38296     // private
38297     onClick : function(e){
38298         if(!this.disabled && this.fireEvent("click", this, e) !== false
38299                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38300             this.handleClick(e);
38301         }else{
38302             e.stopEvent();
38303         }
38304     },
38305
38306     // private
38307     activate : function(){
38308         if(this.disabled){
38309             return false;
38310         }
38311         var li = this.container;
38312         li.addClass(this.activeClass);
38313         this.region = li.getRegion().adjust(2, 2, -2, -2);
38314         this.fireEvent("activate", this);
38315         return true;
38316     },
38317
38318     // private
38319     deactivate : function(){
38320         this.container.removeClass(this.activeClass);
38321         this.fireEvent("deactivate", this);
38322     },
38323
38324     // private
38325     shouldDeactivate : function(e){
38326         return !this.region || !this.region.contains(e.getPoint());
38327     },
38328
38329     // private
38330     handleClick : function(e){
38331         if(this.hideOnClick){
38332             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38333         }
38334     },
38335
38336     // private
38337     expandMenu : function(autoActivate){
38338         // do nothing
38339     },
38340
38341     // private
38342     hideMenu : function(){
38343         // do nothing
38344     }
38345 });/*
38346  * Based on:
38347  * Ext JS Library 1.1.1
38348  * Copyright(c) 2006-2007, Ext JS, LLC.
38349  *
38350  * Originally Released Under LGPL - original licence link has changed is not relivant.
38351  *
38352  * Fork - LGPL
38353  * <script type="text/javascript">
38354  */
38355  
38356 /**
38357  * @class Roo.menu.Adapter
38358  * @extends Roo.menu.BaseItem
38359  * 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.
38360  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38361  * @constructor
38362  * Creates a new Adapter
38363  * @param {Object} config Configuration options
38364  */
38365 Roo.menu.Adapter = function(component, config){
38366     Roo.menu.Adapter.superclass.constructor.call(this, config);
38367     this.component = component;
38368 };
38369 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38370     // private
38371     canActivate : true,
38372
38373     // private
38374     onRender : function(container, position){
38375         this.component.render(container);
38376         this.el = this.component.getEl();
38377     },
38378
38379     // private
38380     activate : function(){
38381         if(this.disabled){
38382             return false;
38383         }
38384         this.component.focus();
38385         this.fireEvent("activate", this);
38386         return true;
38387     },
38388
38389     // private
38390     deactivate : function(){
38391         this.fireEvent("deactivate", this);
38392     },
38393
38394     // private
38395     disable : function(){
38396         this.component.disable();
38397         Roo.menu.Adapter.superclass.disable.call(this);
38398     },
38399
38400     // private
38401     enable : function(){
38402         this.component.enable();
38403         Roo.menu.Adapter.superclass.enable.call(this);
38404     }
38405 });/*
38406  * Based on:
38407  * Ext JS Library 1.1.1
38408  * Copyright(c) 2006-2007, Ext JS, LLC.
38409  *
38410  * Originally Released Under LGPL - original licence link has changed is not relivant.
38411  *
38412  * Fork - LGPL
38413  * <script type="text/javascript">
38414  */
38415
38416 /**
38417  * @class Roo.menu.TextItem
38418  * @extends Roo.menu.BaseItem
38419  * Adds a static text string to a menu, usually used as either a heading or group separator.
38420  * Note: old style constructor with text is still supported.
38421  * 
38422  * @constructor
38423  * Creates a new TextItem
38424  * @param {Object} cfg Configuration
38425  */
38426 Roo.menu.TextItem = function(cfg){
38427     if (typeof(cfg) == 'string') {
38428         this.text = cfg;
38429     } else {
38430         Roo.apply(this,cfg);
38431     }
38432     
38433     Roo.menu.TextItem.superclass.constructor.call(this);
38434 };
38435
38436 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38437     /**
38438      * @cfg {String} text Text to show on item.
38439      */
38440     text : '',
38441     
38442     /**
38443      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38444      */
38445     hideOnClick : false,
38446     /**
38447      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38448      */
38449     itemCls : "x-menu-text",
38450
38451     // private
38452     onRender : function(){
38453         var s = document.createElement("span");
38454         s.className = this.itemCls;
38455         s.innerHTML = this.text;
38456         this.el = s;
38457         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38458     }
38459 });/*
38460  * Based on:
38461  * Ext JS Library 1.1.1
38462  * Copyright(c) 2006-2007, Ext JS, LLC.
38463  *
38464  * Originally Released Under LGPL - original licence link has changed is not relivant.
38465  *
38466  * Fork - LGPL
38467  * <script type="text/javascript">
38468  */
38469
38470 /**
38471  * @class Roo.menu.Separator
38472  * @extends Roo.menu.BaseItem
38473  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38474  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38475  * @constructor
38476  * @param {Object} config Configuration options
38477  */
38478 Roo.menu.Separator = function(config){
38479     Roo.menu.Separator.superclass.constructor.call(this, config);
38480 };
38481
38482 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38483     /**
38484      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38485      */
38486     itemCls : "x-menu-sep",
38487     /**
38488      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38489      */
38490     hideOnClick : false,
38491
38492     // private
38493     onRender : function(li){
38494         var s = document.createElement("span");
38495         s.className = this.itemCls;
38496         s.innerHTML = "&#160;";
38497         this.el = s;
38498         li.addClass("x-menu-sep-li");
38499         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38500     }
38501 });/*
38502  * Based on:
38503  * Ext JS Library 1.1.1
38504  * Copyright(c) 2006-2007, Ext JS, LLC.
38505  *
38506  * Originally Released Under LGPL - original licence link has changed is not relivant.
38507  *
38508  * Fork - LGPL
38509  * <script type="text/javascript">
38510  */
38511 /**
38512  * @class Roo.menu.Item
38513  * @extends Roo.menu.BaseItem
38514  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38515  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38516  * activation and click handling.
38517  * @constructor
38518  * Creates a new Item
38519  * @param {Object} config Configuration options
38520  */
38521 Roo.menu.Item = function(config){
38522     Roo.menu.Item.superclass.constructor.call(this, config);
38523     if(this.menu){
38524         this.menu = Roo.menu.MenuMgr.get(this.menu);
38525     }
38526 };
38527 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38528     
38529     /**
38530      * @cfg {String} text
38531      * The text to show on the menu item.
38532      */
38533     text: '',
38534      /**
38535      * @cfg {String} HTML to render in menu
38536      * The text to show on the menu item (HTML version).
38537      */
38538     html: '',
38539     /**
38540      * @cfg {String} icon
38541      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38542      */
38543     icon: undefined,
38544     /**
38545      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38546      */
38547     itemCls : "x-menu-item",
38548     /**
38549      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38550      */
38551     canActivate : true,
38552     /**
38553      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38554      */
38555     showDelay: 200,
38556     // doc'd in BaseItem
38557     hideDelay: 200,
38558
38559     // private
38560     ctype: "Roo.menu.Item",
38561     
38562     // private
38563     onRender : function(container, position){
38564         var el = document.createElement("a");
38565         el.hideFocus = true;
38566         el.unselectable = "on";
38567         el.href = this.href || "#";
38568         if(this.hrefTarget){
38569             el.target = this.hrefTarget;
38570         }
38571         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38572         
38573         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38574         
38575         el.innerHTML = String.format(
38576                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38577                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38578         this.el = el;
38579         Roo.menu.Item.superclass.onRender.call(this, container, position);
38580     },
38581
38582     /**
38583      * Sets the text to display in this menu item
38584      * @param {String} text The text to display
38585      * @param {Boolean} isHTML true to indicate text is pure html.
38586      */
38587     setText : function(text, isHTML){
38588         if (isHTML) {
38589             this.html = text;
38590         } else {
38591             this.text = text;
38592             this.html = '';
38593         }
38594         if(this.rendered){
38595             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38596      
38597             this.el.update(String.format(
38598                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38599                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38600             this.parentMenu.autoWidth();
38601         }
38602     },
38603
38604     // private
38605     handleClick : function(e){
38606         if(!this.href){ // if no link defined, stop the event automatically
38607             e.stopEvent();
38608         }
38609         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38610     },
38611
38612     // private
38613     activate : function(autoExpand){
38614         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38615             this.focus();
38616             if(autoExpand){
38617                 this.expandMenu();
38618             }
38619         }
38620         return true;
38621     },
38622
38623     // private
38624     shouldDeactivate : function(e){
38625         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38626             if(this.menu && this.menu.isVisible()){
38627                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38628             }
38629             return true;
38630         }
38631         return false;
38632     },
38633
38634     // private
38635     deactivate : function(){
38636         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38637         this.hideMenu();
38638     },
38639
38640     // private
38641     expandMenu : function(autoActivate){
38642         if(!this.disabled && this.menu){
38643             clearTimeout(this.hideTimer);
38644             delete this.hideTimer;
38645             if(!this.menu.isVisible() && !this.showTimer){
38646                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38647             }else if (this.menu.isVisible() && autoActivate){
38648                 this.menu.tryActivate(0, 1);
38649             }
38650         }
38651     },
38652
38653     // private
38654     deferExpand : function(autoActivate){
38655         delete this.showTimer;
38656         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38657         if(autoActivate){
38658             this.menu.tryActivate(0, 1);
38659         }
38660     },
38661
38662     // private
38663     hideMenu : function(){
38664         clearTimeout(this.showTimer);
38665         delete this.showTimer;
38666         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38667             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38668         }
38669     },
38670
38671     // private
38672     deferHide : function(){
38673         delete this.hideTimer;
38674         this.menu.hide();
38675     }
38676 });/*
38677  * Based on:
38678  * Ext JS Library 1.1.1
38679  * Copyright(c) 2006-2007, Ext JS, LLC.
38680  *
38681  * Originally Released Under LGPL - original licence link has changed is not relivant.
38682  *
38683  * Fork - LGPL
38684  * <script type="text/javascript">
38685  */
38686  
38687 /**
38688  * @class Roo.menu.CheckItem
38689  * @extends Roo.menu.Item
38690  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38691  * @constructor
38692  * Creates a new CheckItem
38693  * @param {Object} config Configuration options
38694  */
38695 Roo.menu.CheckItem = function(config){
38696     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38697     this.addEvents({
38698         /**
38699          * @event beforecheckchange
38700          * Fires before the checked value is set, providing an opportunity to cancel if needed
38701          * @param {Roo.menu.CheckItem} this
38702          * @param {Boolean} checked The new checked value that will be set
38703          */
38704         "beforecheckchange" : true,
38705         /**
38706          * @event checkchange
38707          * Fires after the checked value has been set
38708          * @param {Roo.menu.CheckItem} this
38709          * @param {Boolean} checked The checked value that was set
38710          */
38711         "checkchange" : true
38712     });
38713     if(this.checkHandler){
38714         this.on('checkchange', this.checkHandler, this.scope);
38715     }
38716 };
38717 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38718     /**
38719      * @cfg {String} group
38720      * All check items with the same group name will automatically be grouped into a single-select
38721      * radio button group (defaults to '')
38722      */
38723     /**
38724      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38725      */
38726     itemCls : "x-menu-item x-menu-check-item",
38727     /**
38728      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38729      */
38730     groupClass : "x-menu-group-item",
38731
38732     /**
38733      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38734      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38735      * initialized with checked = true will be rendered as checked.
38736      */
38737     checked: false,
38738
38739     // private
38740     ctype: "Roo.menu.CheckItem",
38741
38742     // private
38743     onRender : function(c){
38744         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38745         if(this.group){
38746             this.el.addClass(this.groupClass);
38747         }
38748         Roo.menu.MenuMgr.registerCheckable(this);
38749         if(this.checked){
38750             this.checked = false;
38751             this.setChecked(true, true);
38752         }
38753     },
38754
38755     // private
38756     destroy : function(){
38757         if(this.rendered){
38758             Roo.menu.MenuMgr.unregisterCheckable(this);
38759         }
38760         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38761     },
38762
38763     /**
38764      * Set the checked state of this item
38765      * @param {Boolean} checked The new checked value
38766      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38767      */
38768     setChecked : function(state, suppressEvent){
38769         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38770             if(this.container){
38771                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38772             }
38773             this.checked = state;
38774             if(suppressEvent !== true){
38775                 this.fireEvent("checkchange", this, state);
38776             }
38777         }
38778     },
38779
38780     // private
38781     handleClick : function(e){
38782        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38783            this.setChecked(!this.checked);
38784        }
38785        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38786     }
38787 });/*
38788  * Based on:
38789  * Ext JS Library 1.1.1
38790  * Copyright(c) 2006-2007, Ext JS, LLC.
38791  *
38792  * Originally Released Under LGPL - original licence link has changed is not relivant.
38793  *
38794  * Fork - LGPL
38795  * <script type="text/javascript">
38796  */
38797  
38798 /**
38799  * @class Roo.menu.DateItem
38800  * @extends Roo.menu.Adapter
38801  * A menu item that wraps the {@link Roo.DatPicker} component.
38802  * @constructor
38803  * Creates a new DateItem
38804  * @param {Object} config Configuration options
38805  */
38806 Roo.menu.DateItem = function(config){
38807     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38808     /** The Roo.DatePicker object @type Roo.DatePicker */
38809     this.picker = this.component;
38810     this.addEvents({select: true});
38811     
38812     this.picker.on("render", function(picker){
38813         picker.getEl().swallowEvent("click");
38814         picker.container.addClass("x-menu-date-item");
38815     });
38816
38817     this.picker.on("select", this.onSelect, this);
38818 };
38819
38820 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38821     // private
38822     onSelect : function(picker, date){
38823         this.fireEvent("select", this, date, picker);
38824         Roo.menu.DateItem.superclass.handleClick.call(this);
38825     }
38826 });/*
38827  * Based on:
38828  * Ext JS Library 1.1.1
38829  * Copyright(c) 2006-2007, Ext JS, LLC.
38830  *
38831  * Originally Released Under LGPL - original licence link has changed is not relivant.
38832  *
38833  * Fork - LGPL
38834  * <script type="text/javascript">
38835  */
38836  
38837 /**
38838  * @class Roo.menu.ColorItem
38839  * @extends Roo.menu.Adapter
38840  * A menu item that wraps the {@link Roo.ColorPalette} component.
38841  * @constructor
38842  * Creates a new ColorItem
38843  * @param {Object} config Configuration options
38844  */
38845 Roo.menu.ColorItem = function(config){
38846     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38847     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38848     this.palette = this.component;
38849     this.relayEvents(this.palette, ["select"]);
38850     if(this.selectHandler){
38851         this.on('select', this.selectHandler, this.scope);
38852     }
38853 };
38854 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38855  * Based on:
38856  * Ext JS Library 1.1.1
38857  * Copyright(c) 2006-2007, Ext JS, LLC.
38858  *
38859  * Originally Released Under LGPL - original licence link has changed is not relivant.
38860  *
38861  * Fork - LGPL
38862  * <script type="text/javascript">
38863  */
38864  
38865
38866 /**
38867  * @class Roo.menu.DateMenu
38868  * @extends Roo.menu.Menu
38869  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38870  * @constructor
38871  * Creates a new DateMenu
38872  * @param {Object} config Configuration options
38873  */
38874 Roo.menu.DateMenu = function(config){
38875     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38876     this.plain = true;
38877     var di = new Roo.menu.DateItem(config);
38878     this.add(di);
38879     /**
38880      * The {@link Roo.DatePicker} instance for this DateMenu
38881      * @type DatePicker
38882      */
38883     this.picker = di.picker;
38884     /**
38885      * @event select
38886      * @param {DatePicker} picker
38887      * @param {Date} date
38888      */
38889     this.relayEvents(di, ["select"]);
38890     this.on('beforeshow', function(){
38891         if(this.picker){
38892             this.picker.hideMonthPicker(false);
38893         }
38894     }, this);
38895 };
38896 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38897     cls:'x-date-menu'
38898 });/*
38899  * Based on:
38900  * Ext JS Library 1.1.1
38901  * Copyright(c) 2006-2007, Ext JS, LLC.
38902  *
38903  * Originally Released Under LGPL - original licence link has changed is not relivant.
38904  *
38905  * Fork - LGPL
38906  * <script type="text/javascript">
38907  */
38908  
38909
38910 /**
38911  * @class Roo.menu.ColorMenu
38912  * @extends Roo.menu.Menu
38913  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38914  * @constructor
38915  * Creates a new ColorMenu
38916  * @param {Object} config Configuration options
38917  */
38918 Roo.menu.ColorMenu = function(config){
38919     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38920     this.plain = true;
38921     var ci = new Roo.menu.ColorItem(config);
38922     this.add(ci);
38923     /**
38924      * The {@link Roo.ColorPalette} instance for this ColorMenu
38925      * @type ColorPalette
38926      */
38927     this.palette = ci.palette;
38928     /**
38929      * @event select
38930      * @param {ColorPalette} palette
38931      * @param {String} color
38932      */
38933     this.relayEvents(ci, ["select"]);
38934 };
38935 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38936  * Based on:
38937  * Ext JS Library 1.1.1
38938  * Copyright(c) 2006-2007, Ext JS, LLC.
38939  *
38940  * Originally Released Under LGPL - original licence link has changed is not relivant.
38941  *
38942  * Fork - LGPL
38943  * <script type="text/javascript">
38944  */
38945  
38946 /**
38947  * @class Roo.form.TextItem
38948  * @extends Roo.BoxComponent
38949  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38950  * @constructor
38951  * Creates a new TextItem
38952  * @param {Object} config Configuration options
38953  */
38954 Roo.form.TextItem = function(config){
38955     Roo.form.TextItem.superclass.constructor.call(this, config);
38956 };
38957
38958 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
38959     
38960     /**
38961      * @cfg {String} tag the tag for this item (default div)
38962      */
38963     tag : 'div',
38964     /**
38965      * @cfg {String} html the content for this item
38966      */
38967     html : '',
38968     
38969     getAutoCreate : function()
38970     {
38971         var cfg = {
38972             id: this.id,
38973             tag: this.tag,
38974             html: this.html,
38975             cls: 'x-form-item'
38976         };
38977         
38978         return cfg;
38979         
38980     },
38981     
38982     onRender : function(ct, position)
38983     {
38984         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
38985         
38986         if(!this.el){
38987             var cfg = this.getAutoCreate();
38988             if(!cfg.name){
38989                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38990             }
38991             if (!cfg.name.length) {
38992                 delete cfg.name;
38993             }
38994             this.el = ct.createChild(cfg, position);
38995         }
38996     },
38997     /*
38998      * setHTML
38999      * @param {String} html update the Contents of the element.
39000      */
39001     setHTML : function(html)
39002     {
39003         this.fieldEl.dom.innerHTML = html;
39004     }
39005     
39006 });/*
39007  * Based on:
39008  * Ext JS Library 1.1.1
39009  * Copyright(c) 2006-2007, Ext JS, LLC.
39010  *
39011  * Originally Released Under LGPL - original licence link has changed is not relivant.
39012  *
39013  * Fork - LGPL
39014  * <script type="text/javascript">
39015  */
39016  
39017 /**
39018  * @class Roo.form.Field
39019  * @extends Roo.BoxComponent
39020  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39021  * @constructor
39022  * Creates a new Field
39023  * @param {Object} config Configuration options
39024  */
39025 Roo.form.Field = function(config){
39026     Roo.form.Field.superclass.constructor.call(this, config);
39027 };
39028
39029 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39030     /**
39031      * @cfg {String} fieldLabel Label to use when rendering a form.
39032      */
39033        /**
39034      * @cfg {String} qtip Mouse over tip
39035      */
39036      
39037     /**
39038      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39039      */
39040     invalidClass : "x-form-invalid",
39041     /**
39042      * @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")
39043      */
39044     invalidText : "The value in this field is invalid",
39045     /**
39046      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39047      */
39048     focusClass : "x-form-focus",
39049     /**
39050      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39051       automatic validation (defaults to "keyup").
39052      */
39053     validationEvent : "keyup",
39054     /**
39055      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39056      */
39057     validateOnBlur : true,
39058     /**
39059      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39060      */
39061     validationDelay : 250,
39062     /**
39063      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39064      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39065      */
39066     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39067     /**
39068      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39069      */
39070     fieldClass : "x-form-field",
39071     /**
39072      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
39073      *<pre>
39074 Value         Description
39075 -----------   ----------------------------------------------------------------------
39076 qtip          Display a quick tip when the user hovers over the field
39077 title         Display a default browser title attribute popup
39078 under         Add a block div beneath the field containing the error text
39079 side          Add an error icon to the right of the field with a popup on hover
39080 [element id]  Add the error text directly to the innerHTML of the specified element
39081 </pre>
39082      */
39083     msgTarget : 'qtip',
39084     /**
39085      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
39086      */
39087     msgFx : 'normal',
39088
39089     /**
39090      * @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.
39091      */
39092     readOnly : false,
39093
39094     /**
39095      * @cfg {Boolean} disabled True to disable the field (defaults to false).
39096      */
39097     disabled : false,
39098
39099     /**
39100      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
39101      */
39102     inputType : undefined,
39103     
39104     /**
39105      * @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).
39106          */
39107         tabIndex : undefined,
39108         
39109     // private
39110     isFormField : true,
39111
39112     // private
39113     hasFocus : false,
39114     /**
39115      * @property {Roo.Element} fieldEl
39116      * Element Containing the rendered Field (with label etc.)
39117      */
39118     /**
39119      * @cfg {Mixed} value A value to initialize this field with.
39120      */
39121     value : undefined,
39122
39123     /**
39124      * @cfg {String} name The field's HTML name attribute.
39125      */
39126     /**
39127      * @cfg {String} cls A CSS class to apply to the field's underlying element.
39128      */
39129     // private
39130     loadedValue : false,
39131      
39132      
39133         // private ??
39134         initComponent : function(){
39135         Roo.form.Field.superclass.initComponent.call(this);
39136         this.addEvents({
39137             /**
39138              * @event focus
39139              * Fires when this field receives input focus.
39140              * @param {Roo.form.Field} this
39141              */
39142             focus : true,
39143             /**
39144              * @event blur
39145              * Fires when this field loses input focus.
39146              * @param {Roo.form.Field} this
39147              */
39148             blur : true,
39149             /**
39150              * @event specialkey
39151              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
39152              * {@link Roo.EventObject#getKey} to determine which key was pressed.
39153              * @param {Roo.form.Field} this
39154              * @param {Roo.EventObject} e The event object
39155              */
39156             specialkey : true,
39157             /**
39158              * @event change
39159              * Fires just before the field blurs if the field value has changed.
39160              * @param {Roo.form.Field} this
39161              * @param {Mixed} newValue The new value
39162              * @param {Mixed} oldValue The original value
39163              */
39164             change : true,
39165             /**
39166              * @event invalid
39167              * Fires after the field has been marked as invalid.
39168              * @param {Roo.form.Field} this
39169              * @param {String} msg The validation message
39170              */
39171             invalid : true,
39172             /**
39173              * @event valid
39174              * Fires after the field has been validated with no errors.
39175              * @param {Roo.form.Field} this
39176              */
39177             valid : true,
39178              /**
39179              * @event keyup
39180              * Fires after the key up
39181              * @param {Roo.form.Field} this
39182              * @param {Roo.EventObject}  e The event Object
39183              */
39184             keyup : true
39185         });
39186     },
39187
39188     /**
39189      * Returns the name attribute of the field if available
39190      * @return {String} name The field name
39191      */
39192     getName: function(){
39193          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
39194     },
39195
39196     // private
39197     onRender : function(ct, position){
39198         Roo.form.Field.superclass.onRender.call(this, ct, position);
39199         if(!this.el){
39200             var cfg = this.getAutoCreate();
39201             if(!cfg.name){
39202                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39203             }
39204             if (!cfg.name.length) {
39205                 delete cfg.name;
39206             }
39207             if(this.inputType){
39208                 cfg.type = this.inputType;
39209             }
39210             this.el = ct.createChild(cfg, position);
39211         }
39212         var type = this.el.dom.type;
39213         if(type){
39214             if(type == 'password'){
39215                 type = 'text';
39216             }
39217             this.el.addClass('x-form-'+type);
39218         }
39219         if(this.readOnly){
39220             this.el.dom.readOnly = true;
39221         }
39222         if(this.tabIndex !== undefined){
39223             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39224         }
39225
39226         this.el.addClass([this.fieldClass, this.cls]);
39227         this.initValue();
39228     },
39229
39230     /**
39231      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39232      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39233      * @return {Roo.form.Field} this
39234      */
39235     applyTo : function(target){
39236         this.allowDomMove = false;
39237         this.el = Roo.get(target);
39238         this.render(this.el.dom.parentNode);
39239         return this;
39240     },
39241
39242     // private
39243     initValue : function(){
39244         if(this.value !== undefined){
39245             this.setValue(this.value);
39246         }else if(this.el.dom.value.length > 0){
39247             this.setValue(this.el.dom.value);
39248         }
39249     },
39250
39251     /**
39252      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39253      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39254      */
39255     isDirty : function() {
39256         if(this.disabled) {
39257             return false;
39258         }
39259         return String(this.getValue()) !== String(this.originalValue);
39260     },
39261
39262     /**
39263      * stores the current value in loadedValue
39264      */
39265     resetHasChanged : function()
39266     {
39267         this.loadedValue = String(this.getValue());
39268     },
39269     /**
39270      * checks the current value against the 'loaded' value.
39271      * Note - will return false if 'resetHasChanged' has not been called first.
39272      */
39273     hasChanged : function()
39274     {
39275         if(this.disabled || this.readOnly) {
39276             return false;
39277         }
39278         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39279     },
39280     
39281     
39282     
39283     // private
39284     afterRender : function(){
39285         Roo.form.Field.superclass.afterRender.call(this);
39286         this.initEvents();
39287     },
39288
39289     // private
39290     fireKey : function(e){
39291         //Roo.log('field ' + e.getKey());
39292         if(e.isNavKeyPress()){
39293             this.fireEvent("specialkey", this, e);
39294         }
39295     },
39296
39297     /**
39298      * Resets the current field value to the originally loaded value and clears any validation messages
39299      */
39300     reset : function(){
39301         this.setValue(this.resetValue);
39302         this.originalValue = this.getValue();
39303         this.clearInvalid();
39304     },
39305
39306     // private
39307     initEvents : function(){
39308         // safari killled keypress - so keydown is now used..
39309         this.el.on("keydown" , this.fireKey,  this);
39310         this.el.on("focus", this.onFocus,  this);
39311         this.el.on("blur", this.onBlur,  this);
39312         this.el.relayEvent('keyup', this);
39313
39314         // reference to original value for reset
39315         this.originalValue = this.getValue();
39316         this.resetValue =  this.getValue();
39317     },
39318
39319     // private
39320     onFocus : function(){
39321         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39322             this.el.addClass(this.focusClass);
39323         }
39324         if(!this.hasFocus){
39325             this.hasFocus = true;
39326             this.startValue = this.getValue();
39327             this.fireEvent("focus", this);
39328         }
39329     },
39330
39331     beforeBlur : Roo.emptyFn,
39332
39333     // private
39334     onBlur : function(){
39335         this.beforeBlur();
39336         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39337             this.el.removeClass(this.focusClass);
39338         }
39339         this.hasFocus = false;
39340         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39341             this.validate();
39342         }
39343         var v = this.getValue();
39344         if(String(v) !== String(this.startValue)){
39345             this.fireEvent('change', this, v, this.startValue);
39346         }
39347         this.fireEvent("blur", this);
39348     },
39349
39350     /**
39351      * Returns whether or not the field value is currently valid
39352      * @param {Boolean} preventMark True to disable marking the field invalid
39353      * @return {Boolean} True if the value is valid, else false
39354      */
39355     isValid : function(preventMark){
39356         if(this.disabled){
39357             return true;
39358         }
39359         var restore = this.preventMark;
39360         this.preventMark = preventMark === true;
39361         var v = this.validateValue(this.processValue(this.getRawValue()));
39362         this.preventMark = restore;
39363         return v;
39364     },
39365
39366     /**
39367      * Validates the field value
39368      * @return {Boolean} True if the value is valid, else false
39369      */
39370     validate : function(){
39371         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39372             this.clearInvalid();
39373             return true;
39374         }
39375         return false;
39376     },
39377
39378     processValue : function(value){
39379         return value;
39380     },
39381
39382     // private
39383     // Subclasses should provide the validation implementation by overriding this
39384     validateValue : function(value){
39385         return true;
39386     },
39387
39388     /**
39389      * Mark this field as invalid
39390      * @param {String} msg The validation message
39391      */
39392     markInvalid : function(msg){
39393         if(!this.rendered || this.preventMark){ // not rendered
39394             return;
39395         }
39396         
39397         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39398         
39399         obj.el.addClass(this.invalidClass);
39400         msg = msg || this.invalidText;
39401         switch(this.msgTarget){
39402             case 'qtip':
39403                 obj.el.dom.qtip = msg;
39404                 obj.el.dom.qclass = 'x-form-invalid-tip';
39405                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39406                     Roo.QuickTips.enable();
39407                 }
39408                 break;
39409             case 'title':
39410                 this.el.dom.title = msg;
39411                 break;
39412             case 'under':
39413                 if(!this.errorEl){
39414                     var elp = this.el.findParent('.x-form-element', 5, true);
39415                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39416                     this.errorEl.setWidth(elp.getWidth(true)-20);
39417                 }
39418                 this.errorEl.update(msg);
39419                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39420                 break;
39421             case 'side':
39422                 if(!this.errorIcon){
39423                     var elp = this.el.findParent('.x-form-element', 5, true);
39424                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39425                 }
39426                 this.alignErrorIcon();
39427                 this.errorIcon.dom.qtip = msg;
39428                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39429                 this.errorIcon.show();
39430                 this.on('resize', this.alignErrorIcon, this);
39431                 break;
39432             default:
39433                 var t = Roo.getDom(this.msgTarget);
39434                 t.innerHTML = msg;
39435                 t.style.display = this.msgDisplay;
39436                 break;
39437         }
39438         this.fireEvent('invalid', this, msg);
39439     },
39440
39441     // private
39442     alignErrorIcon : function(){
39443         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39444     },
39445
39446     /**
39447      * Clear any invalid styles/messages for this field
39448      */
39449     clearInvalid : function(){
39450         if(!this.rendered || this.preventMark){ // not rendered
39451             return;
39452         }
39453         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39454         
39455         obj.el.removeClass(this.invalidClass);
39456         switch(this.msgTarget){
39457             case 'qtip':
39458                 obj.el.dom.qtip = '';
39459                 break;
39460             case 'title':
39461                 this.el.dom.title = '';
39462                 break;
39463             case 'under':
39464                 if(this.errorEl){
39465                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39466                 }
39467                 break;
39468             case 'side':
39469                 if(this.errorIcon){
39470                     this.errorIcon.dom.qtip = '';
39471                     this.errorIcon.hide();
39472                     this.un('resize', this.alignErrorIcon, this);
39473                 }
39474                 break;
39475             default:
39476                 var t = Roo.getDom(this.msgTarget);
39477                 t.innerHTML = '';
39478                 t.style.display = 'none';
39479                 break;
39480         }
39481         this.fireEvent('valid', this);
39482     },
39483
39484     /**
39485      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39486      * @return {Mixed} value The field value
39487      */
39488     getRawValue : function(){
39489         var v = this.el.getValue();
39490         
39491         return v;
39492     },
39493
39494     /**
39495      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39496      * @return {Mixed} value The field value
39497      */
39498     getValue : function(){
39499         var v = this.el.getValue();
39500          
39501         return v;
39502     },
39503
39504     /**
39505      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39506      * @param {Mixed} value The value to set
39507      */
39508     setRawValue : function(v){
39509         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39510     },
39511
39512     /**
39513      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39514      * @param {Mixed} value The value to set
39515      */
39516     setValue : function(v){
39517         this.value = v;
39518         if(this.rendered){
39519             this.el.dom.value = (v === null || v === undefined ? '' : v);
39520              this.validate();
39521         }
39522     },
39523
39524     adjustSize : function(w, h){
39525         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39526         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39527         return s;
39528     },
39529
39530     adjustWidth : function(tag, w){
39531         tag = tag.toLowerCase();
39532         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39533             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39534                 if(tag == 'input'){
39535                     return w + 2;
39536                 }
39537                 if(tag == 'textarea'){
39538                     return w-2;
39539                 }
39540             }else if(Roo.isOpera){
39541                 if(tag == 'input'){
39542                     return w + 2;
39543                 }
39544                 if(tag == 'textarea'){
39545                     return w-2;
39546                 }
39547             }
39548         }
39549         return w;
39550     }
39551 });
39552
39553
39554 // anything other than normal should be considered experimental
39555 Roo.form.Field.msgFx = {
39556     normal : {
39557         show: function(msgEl, f){
39558             msgEl.setDisplayed('block');
39559         },
39560
39561         hide : function(msgEl, f){
39562             msgEl.setDisplayed(false).update('');
39563         }
39564     },
39565
39566     slide : {
39567         show: function(msgEl, f){
39568             msgEl.slideIn('t', {stopFx:true});
39569         },
39570
39571         hide : function(msgEl, f){
39572             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39573         }
39574     },
39575
39576     slideRight : {
39577         show: function(msgEl, f){
39578             msgEl.fixDisplay();
39579             msgEl.alignTo(f.el, 'tl-tr');
39580             msgEl.slideIn('l', {stopFx:true});
39581         },
39582
39583         hide : function(msgEl, f){
39584             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39585         }
39586     }
39587 };/*
39588  * Based on:
39589  * Ext JS Library 1.1.1
39590  * Copyright(c) 2006-2007, Ext JS, LLC.
39591  *
39592  * Originally Released Under LGPL - original licence link has changed is not relivant.
39593  *
39594  * Fork - LGPL
39595  * <script type="text/javascript">
39596  */
39597  
39598
39599 /**
39600  * @class Roo.form.TextField
39601  * @extends Roo.form.Field
39602  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39603  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39604  * @constructor
39605  * Creates a new TextField
39606  * @param {Object} config Configuration options
39607  */
39608 Roo.form.TextField = function(config){
39609     Roo.form.TextField.superclass.constructor.call(this, config);
39610     this.addEvents({
39611         /**
39612          * @event autosize
39613          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39614          * according to the default logic, but this event provides a hook for the developer to apply additional
39615          * logic at runtime to resize the field if needed.
39616              * @param {Roo.form.Field} this This text field
39617              * @param {Number} width The new field width
39618              */
39619         autosize : true
39620     });
39621 };
39622
39623 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39624     /**
39625      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39626      */
39627     grow : false,
39628     /**
39629      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39630      */
39631     growMin : 30,
39632     /**
39633      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39634      */
39635     growMax : 800,
39636     /**
39637      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39638      */
39639     vtype : null,
39640     /**
39641      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39642      */
39643     maskRe : null,
39644     /**
39645      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39646      */
39647     disableKeyFilter : false,
39648     /**
39649      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39650      */
39651     allowBlank : true,
39652     /**
39653      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39654      */
39655     minLength : 0,
39656     /**
39657      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39658      */
39659     maxLength : Number.MAX_VALUE,
39660     /**
39661      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39662      */
39663     minLengthText : "The minimum length for this field is {0}",
39664     /**
39665      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39666      */
39667     maxLengthText : "The maximum length for this field is {0}",
39668     /**
39669      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39670      */
39671     selectOnFocus : false,
39672     /**
39673      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39674      */    
39675     allowLeadingSpace : false,
39676     /**
39677      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39678      */
39679     blankText : "This field is required",
39680     /**
39681      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39682      * If available, this function will be called only after the basic validators all return true, and will be passed the
39683      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39684      */
39685     validator : null,
39686     /**
39687      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39688      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39689      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39690      */
39691     regex : null,
39692     /**
39693      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39694      */
39695     regexText : "",
39696     /**
39697      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39698      */
39699     emptyText : null,
39700    
39701
39702     // private
39703     initEvents : function()
39704     {
39705         if (this.emptyText) {
39706             this.el.attr('placeholder', this.emptyText);
39707         }
39708         
39709         Roo.form.TextField.superclass.initEvents.call(this);
39710         if(this.validationEvent == 'keyup'){
39711             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39712             this.el.on('keyup', this.filterValidation, this);
39713         }
39714         else if(this.validationEvent !== false){
39715             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39716         }
39717         
39718         if(this.selectOnFocus){
39719             this.on("focus", this.preFocus, this);
39720         }
39721         if (!this.allowLeadingSpace) {
39722             this.on('blur', this.cleanLeadingSpace, this);
39723         }
39724         
39725         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39726             this.el.on("keypress", this.filterKeys, this);
39727         }
39728         if(this.grow){
39729             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39730             this.el.on("click", this.autoSize,  this);
39731         }
39732         if(this.el.is('input[type=password]') && Roo.isSafari){
39733             this.el.on('keydown', this.SafariOnKeyDown, this);
39734         }
39735     },
39736
39737     processValue : function(value){
39738         if(this.stripCharsRe){
39739             var newValue = value.replace(this.stripCharsRe, '');
39740             if(newValue !== value){
39741                 this.setRawValue(newValue);
39742                 return newValue;
39743             }
39744         }
39745         return value;
39746     },
39747
39748     filterValidation : function(e){
39749         if(!e.isNavKeyPress()){
39750             this.validationTask.delay(this.validationDelay);
39751         }
39752     },
39753
39754     // private
39755     onKeyUp : function(e){
39756         if(!e.isNavKeyPress()){
39757             this.autoSize();
39758         }
39759     },
39760     // private - clean the leading white space
39761     cleanLeadingSpace : function(e)
39762     {
39763         if ( this.inputType == 'file') {
39764             return;
39765         }
39766         
39767         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39768     },
39769     /**
39770      * Resets the current field value to the originally-loaded value and clears any validation messages.
39771      *  
39772      */
39773     reset : function(){
39774         Roo.form.TextField.superclass.reset.call(this);
39775        
39776     }, 
39777     // private
39778     preFocus : function(){
39779         
39780         if(this.selectOnFocus){
39781             this.el.dom.select();
39782         }
39783     },
39784
39785     
39786     // private
39787     filterKeys : function(e){
39788         var k = e.getKey();
39789         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39790             return;
39791         }
39792         var c = e.getCharCode(), cc = String.fromCharCode(c);
39793         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39794             return;
39795         }
39796         if(!this.maskRe.test(cc)){
39797             e.stopEvent();
39798         }
39799     },
39800
39801     setValue : function(v){
39802         
39803         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39804         
39805         this.autoSize();
39806     },
39807
39808     /**
39809      * Validates a value according to the field's validation rules and marks the field as invalid
39810      * if the validation fails
39811      * @param {Mixed} value The value to validate
39812      * @return {Boolean} True if the value is valid, else false
39813      */
39814     validateValue : function(value){
39815         if(value.length < 1)  { // if it's blank
39816              if(this.allowBlank){
39817                 this.clearInvalid();
39818                 return true;
39819              }else{
39820                 this.markInvalid(this.blankText);
39821                 return false;
39822              }
39823         }
39824         if(value.length < this.minLength){
39825             this.markInvalid(String.format(this.minLengthText, this.minLength));
39826             return false;
39827         }
39828         if(value.length > this.maxLength){
39829             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39830             return false;
39831         }
39832         if(this.vtype){
39833             var vt = Roo.form.VTypes;
39834             if(!vt[this.vtype](value, this)){
39835                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39836                 return false;
39837             }
39838         }
39839         if(typeof this.validator == "function"){
39840             var msg = this.validator(value);
39841             if(msg !== true){
39842                 this.markInvalid(msg);
39843                 return false;
39844             }
39845         }
39846         if(this.regex && !this.regex.test(value)){
39847             this.markInvalid(this.regexText);
39848             return false;
39849         }
39850         return true;
39851     },
39852
39853     /**
39854      * Selects text in this field
39855      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39856      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39857      */
39858     selectText : function(start, end){
39859         var v = this.getRawValue();
39860         if(v.length > 0){
39861             start = start === undefined ? 0 : start;
39862             end = end === undefined ? v.length : end;
39863             var d = this.el.dom;
39864             if(d.setSelectionRange){
39865                 d.setSelectionRange(start, end);
39866             }else if(d.createTextRange){
39867                 var range = d.createTextRange();
39868                 range.moveStart("character", start);
39869                 range.moveEnd("character", v.length-end);
39870                 range.select();
39871             }
39872         }
39873     },
39874
39875     /**
39876      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39877      * This only takes effect if grow = true, and fires the autosize event.
39878      */
39879     autoSize : function(){
39880         if(!this.grow || !this.rendered){
39881             return;
39882         }
39883         if(!this.metrics){
39884             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39885         }
39886         var el = this.el;
39887         var v = el.dom.value;
39888         var d = document.createElement('div');
39889         d.appendChild(document.createTextNode(v));
39890         v = d.innerHTML;
39891         d = null;
39892         v += "&#160;";
39893         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39894         this.el.setWidth(w);
39895         this.fireEvent("autosize", this, w);
39896     },
39897     
39898     // private
39899     SafariOnKeyDown : function(event)
39900     {
39901         // this is a workaround for a password hang bug on chrome/ webkit.
39902         
39903         var isSelectAll = false;
39904         
39905         if(this.el.dom.selectionEnd > 0){
39906             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39907         }
39908         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39909             event.preventDefault();
39910             this.setValue('');
39911             return;
39912         }
39913         
39914         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39915             
39916             event.preventDefault();
39917             // this is very hacky as keydown always get's upper case.
39918             
39919             var cc = String.fromCharCode(event.getCharCode());
39920             
39921             
39922             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39923             
39924         }
39925         
39926         
39927     }
39928 });/*
39929  * Based on:
39930  * Ext JS Library 1.1.1
39931  * Copyright(c) 2006-2007, Ext JS, LLC.
39932  *
39933  * Originally Released Under LGPL - original licence link has changed is not relivant.
39934  *
39935  * Fork - LGPL
39936  * <script type="text/javascript">
39937  */
39938  
39939 /**
39940  * @class Roo.form.Hidden
39941  * @extends Roo.form.TextField
39942  * Simple Hidden element used on forms 
39943  * 
39944  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39945  * 
39946  * @constructor
39947  * Creates a new Hidden form element.
39948  * @param {Object} config Configuration options
39949  */
39950
39951
39952
39953 // easy hidden field...
39954 Roo.form.Hidden = function(config){
39955     Roo.form.Hidden.superclass.constructor.call(this, config);
39956 };
39957   
39958 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39959     fieldLabel:      '',
39960     inputType:      'hidden',
39961     width:          50,
39962     allowBlank:     true,
39963     labelSeparator: '',
39964     hidden:         true,
39965     itemCls :       'x-form-item-display-none'
39966
39967
39968 });
39969
39970
39971 /*
39972  * Based on:
39973  * Ext JS Library 1.1.1
39974  * Copyright(c) 2006-2007, Ext JS, LLC.
39975  *
39976  * Originally Released Under LGPL - original licence link has changed is not relivant.
39977  *
39978  * Fork - LGPL
39979  * <script type="text/javascript">
39980  */
39981  
39982 /**
39983  * @class Roo.form.TriggerField
39984  * @extends Roo.form.TextField
39985  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39986  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39987  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39988  * for which you can provide a custom implementation.  For example:
39989  * <pre><code>
39990 var trigger = new Roo.form.TriggerField();
39991 trigger.onTriggerClick = myTriggerFn;
39992 trigger.applyTo('my-field');
39993 </code></pre>
39994  *
39995  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39996  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39997  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39998  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39999  * @constructor
40000  * Create a new TriggerField.
40001  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40002  * to the base TextField)
40003  */
40004 Roo.form.TriggerField = function(config){
40005     this.mimicing = false;
40006     Roo.form.TriggerField.superclass.constructor.call(this, config);
40007 };
40008
40009 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40010     /**
40011      * @cfg {String} triggerClass A CSS class to apply to the trigger
40012      */
40013     /**
40014      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40015      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40016      */
40017     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40018     /**
40019      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40020      */
40021     hideTrigger:false,
40022
40023     /** @cfg {Boolean} grow @hide */
40024     /** @cfg {Number} growMin @hide */
40025     /** @cfg {Number} growMax @hide */
40026
40027     /**
40028      * @hide 
40029      * @method
40030      */
40031     autoSize: Roo.emptyFn,
40032     // private
40033     monitorTab : true,
40034     // private
40035     deferHeight : true,
40036
40037     
40038     actionMode : 'wrap',
40039     // private
40040     onResize : function(w, h){
40041         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40042         if(typeof w == 'number'){
40043             var x = w - this.trigger.getWidth();
40044             this.el.setWidth(this.adjustWidth('input', x));
40045             this.trigger.setStyle('left', x+'px');
40046         }
40047     },
40048
40049     // private
40050     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40051
40052     // private
40053     getResizeEl : function(){
40054         return this.wrap;
40055     },
40056
40057     // private
40058     getPositionEl : function(){
40059         return this.wrap;
40060     },
40061
40062     // private
40063     alignErrorIcon : function(){
40064         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40065     },
40066
40067     // private
40068     onRender : function(ct, position){
40069         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40070         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
40071         this.trigger = this.wrap.createChild(this.triggerConfig ||
40072                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
40073         if(this.hideTrigger){
40074             this.trigger.setDisplayed(false);
40075         }
40076         this.initTrigger();
40077         if(!this.width){
40078             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
40079         }
40080     },
40081
40082     // private
40083     initTrigger : function(){
40084         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40085         this.trigger.addClassOnOver('x-form-trigger-over');
40086         this.trigger.addClassOnClick('x-form-trigger-click');
40087     },
40088
40089     // private
40090     onDestroy : function(){
40091         if(this.trigger){
40092             this.trigger.removeAllListeners();
40093             this.trigger.remove();
40094         }
40095         if(this.wrap){
40096             this.wrap.remove();
40097         }
40098         Roo.form.TriggerField.superclass.onDestroy.call(this);
40099     },
40100
40101     // private
40102     onFocus : function(){
40103         Roo.form.TriggerField.superclass.onFocus.call(this);
40104         if(!this.mimicing){
40105             this.wrap.addClass('x-trigger-wrap-focus');
40106             this.mimicing = true;
40107             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
40108             if(this.monitorTab){
40109                 this.el.on("keydown", this.checkTab, this);
40110             }
40111         }
40112     },
40113
40114     // private
40115     checkTab : function(e){
40116         if(e.getKey() == e.TAB){
40117             this.triggerBlur();
40118         }
40119     },
40120
40121     // private
40122     onBlur : function(){
40123         // do nothing
40124     },
40125
40126     // private
40127     mimicBlur : function(e, t){
40128         if(!this.wrap.contains(t) && this.validateBlur()){
40129             this.triggerBlur();
40130         }
40131     },
40132
40133     // private
40134     triggerBlur : function(){
40135         this.mimicing = false;
40136         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40137         if(this.monitorTab){
40138             this.el.un("keydown", this.checkTab, this);
40139         }
40140         this.wrap.removeClass('x-trigger-wrap-focus');
40141         Roo.form.TriggerField.superclass.onBlur.call(this);
40142     },
40143
40144     // private
40145     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40146     validateBlur : function(e, t){
40147         return true;
40148     },
40149
40150     // private
40151     onDisable : function(){
40152         Roo.form.TriggerField.superclass.onDisable.call(this);
40153         if(this.wrap){
40154             this.wrap.addClass('x-item-disabled');
40155         }
40156     },
40157
40158     // private
40159     onEnable : function(){
40160         Roo.form.TriggerField.superclass.onEnable.call(this);
40161         if(this.wrap){
40162             this.wrap.removeClass('x-item-disabled');
40163         }
40164     },
40165
40166     // private
40167     onShow : function(){
40168         var ae = this.getActionEl();
40169         
40170         if(ae){
40171             ae.dom.style.display = '';
40172             ae.dom.style.visibility = 'visible';
40173         }
40174     },
40175
40176     // private
40177     
40178     onHide : function(){
40179         var ae = this.getActionEl();
40180         ae.dom.style.display = 'none';
40181     },
40182
40183     /**
40184      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
40185      * by an implementing function.
40186      * @method
40187      * @param {EventObject} e
40188      */
40189     onTriggerClick : Roo.emptyFn
40190 });
40191
40192 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
40193 // to be extended by an implementing class.  For an example of implementing this class, see the custom
40194 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
40195 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
40196     initComponent : function(){
40197         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
40198
40199         this.triggerConfig = {
40200             tag:'span', cls:'x-form-twin-triggers', cn:[
40201             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
40202             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
40203         ]};
40204     },
40205
40206     getTrigger : function(index){
40207         return this.triggers[index];
40208     },
40209
40210     initTrigger : function(){
40211         var ts = this.trigger.select('.x-form-trigger', true);
40212         this.wrap.setStyle('overflow', 'hidden');
40213         var triggerField = this;
40214         ts.each(function(t, all, index){
40215             t.hide = function(){
40216                 var w = triggerField.wrap.getWidth();
40217                 this.dom.style.display = 'none';
40218                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40219             };
40220             t.show = function(){
40221                 var w = triggerField.wrap.getWidth();
40222                 this.dom.style.display = '';
40223                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40224             };
40225             var triggerIndex = 'Trigger'+(index+1);
40226
40227             if(this['hide'+triggerIndex]){
40228                 t.dom.style.display = 'none';
40229             }
40230             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40231             t.addClassOnOver('x-form-trigger-over');
40232             t.addClassOnClick('x-form-trigger-click');
40233         }, this);
40234         this.triggers = ts.elements;
40235     },
40236
40237     onTrigger1Click : Roo.emptyFn,
40238     onTrigger2Click : Roo.emptyFn
40239 });/*
40240  * Based on:
40241  * Ext JS Library 1.1.1
40242  * Copyright(c) 2006-2007, Ext JS, LLC.
40243  *
40244  * Originally Released Under LGPL - original licence link has changed is not relivant.
40245  *
40246  * Fork - LGPL
40247  * <script type="text/javascript">
40248  */
40249  
40250 /**
40251  * @class Roo.form.TextArea
40252  * @extends Roo.form.TextField
40253  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40254  * support for auto-sizing.
40255  * @constructor
40256  * Creates a new TextArea
40257  * @param {Object} config Configuration options
40258  */
40259 Roo.form.TextArea = function(config){
40260     Roo.form.TextArea.superclass.constructor.call(this, config);
40261     // these are provided exchanges for backwards compat
40262     // minHeight/maxHeight were replaced by growMin/growMax to be
40263     // compatible with TextField growing config values
40264     if(this.minHeight !== undefined){
40265         this.growMin = this.minHeight;
40266     }
40267     if(this.maxHeight !== undefined){
40268         this.growMax = this.maxHeight;
40269     }
40270 };
40271
40272 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40273     /**
40274      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40275      */
40276     growMin : 60,
40277     /**
40278      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40279      */
40280     growMax: 1000,
40281     /**
40282      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40283      * in the field (equivalent to setting overflow: hidden, defaults to false)
40284      */
40285     preventScrollbars: false,
40286     /**
40287      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40288      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40289      */
40290
40291     // private
40292     onRender : function(ct, position){
40293         if(!this.el){
40294             this.defaultAutoCreate = {
40295                 tag: "textarea",
40296                 style:"width:300px;height:60px;",
40297                 autocomplete: "new-password"
40298             };
40299         }
40300         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40301         if(this.grow){
40302             this.textSizeEl = Roo.DomHelper.append(document.body, {
40303                 tag: "pre", cls: "x-form-grow-sizer"
40304             });
40305             if(this.preventScrollbars){
40306                 this.el.setStyle("overflow", "hidden");
40307             }
40308             this.el.setHeight(this.growMin);
40309         }
40310     },
40311
40312     onDestroy : function(){
40313         if(this.textSizeEl){
40314             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40315         }
40316         Roo.form.TextArea.superclass.onDestroy.call(this);
40317     },
40318
40319     // private
40320     onKeyUp : function(e){
40321         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40322             this.autoSize();
40323         }
40324     },
40325
40326     /**
40327      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40328      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40329      */
40330     autoSize : function(){
40331         if(!this.grow || !this.textSizeEl){
40332             return;
40333         }
40334         var el = this.el;
40335         var v = el.dom.value;
40336         var ts = this.textSizeEl;
40337
40338         ts.innerHTML = '';
40339         ts.appendChild(document.createTextNode(v));
40340         v = ts.innerHTML;
40341
40342         Roo.fly(ts).setWidth(this.el.getWidth());
40343         if(v.length < 1){
40344             v = "&#160;&#160;";
40345         }else{
40346             if(Roo.isIE){
40347                 v = v.replace(/\n/g, '<p>&#160;</p>');
40348             }
40349             v += "&#160;\n&#160;";
40350         }
40351         ts.innerHTML = v;
40352         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40353         if(h != this.lastHeight){
40354             this.lastHeight = h;
40355             this.el.setHeight(h);
40356             this.fireEvent("autosize", this, h);
40357         }
40358     }
40359 });/*
40360  * Based on:
40361  * Ext JS Library 1.1.1
40362  * Copyright(c) 2006-2007, Ext JS, LLC.
40363  *
40364  * Originally Released Under LGPL - original licence link has changed is not relivant.
40365  *
40366  * Fork - LGPL
40367  * <script type="text/javascript">
40368  */
40369  
40370
40371 /**
40372  * @class Roo.form.NumberField
40373  * @extends Roo.form.TextField
40374  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40375  * @constructor
40376  * Creates a new NumberField
40377  * @param {Object} config Configuration options
40378  */
40379 Roo.form.NumberField = function(config){
40380     Roo.form.NumberField.superclass.constructor.call(this, config);
40381 };
40382
40383 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40384     /**
40385      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40386      */
40387     fieldClass: "x-form-field x-form-num-field",
40388     /**
40389      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40390      */
40391     allowDecimals : true,
40392     /**
40393      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40394      */
40395     decimalSeparator : ".",
40396     /**
40397      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40398      */
40399     decimalPrecision : 2,
40400     /**
40401      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40402      */
40403     allowNegative : true,
40404     /**
40405      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40406      */
40407     minValue : Number.NEGATIVE_INFINITY,
40408     /**
40409      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40410      */
40411     maxValue : Number.MAX_VALUE,
40412     /**
40413      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40414      */
40415     minText : "The minimum value for this field is {0}",
40416     /**
40417      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40418      */
40419     maxText : "The maximum value for this field is {0}",
40420     /**
40421      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40422      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40423      */
40424     nanText : "{0} is not a valid number",
40425
40426     // private
40427     initEvents : function(){
40428         Roo.form.NumberField.superclass.initEvents.call(this);
40429         var allowed = "0123456789";
40430         if(this.allowDecimals){
40431             allowed += this.decimalSeparator;
40432         }
40433         if(this.allowNegative){
40434             allowed += "-";
40435         }
40436         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40437         var keyPress = function(e){
40438             var k = e.getKey();
40439             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40440                 return;
40441             }
40442             var c = e.getCharCode();
40443             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40444                 e.stopEvent();
40445             }
40446         };
40447         this.el.on("keypress", keyPress, this);
40448     },
40449
40450     // private
40451     validateValue : function(value){
40452         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40453             return false;
40454         }
40455         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40456              return true;
40457         }
40458         var num = this.parseValue(value);
40459         if(isNaN(num)){
40460             this.markInvalid(String.format(this.nanText, value));
40461             return false;
40462         }
40463         if(num < this.minValue){
40464             this.markInvalid(String.format(this.minText, this.minValue));
40465             return false;
40466         }
40467         if(num > this.maxValue){
40468             this.markInvalid(String.format(this.maxText, this.maxValue));
40469             return false;
40470         }
40471         return true;
40472     },
40473
40474     getValue : function(){
40475         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40476     },
40477
40478     // private
40479     parseValue : function(value){
40480         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40481         return isNaN(value) ? '' : value;
40482     },
40483
40484     // private
40485     fixPrecision : function(value){
40486         var nan = isNaN(value);
40487         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40488             return nan ? '' : value;
40489         }
40490         return parseFloat(value).toFixed(this.decimalPrecision);
40491     },
40492
40493     setValue : function(v){
40494         v = this.fixPrecision(v);
40495         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40496     },
40497
40498     // private
40499     decimalPrecisionFcn : function(v){
40500         return Math.floor(v);
40501     },
40502
40503     beforeBlur : function(){
40504         var v = this.parseValue(this.getRawValue());
40505         if(v){
40506             this.setValue(v);
40507         }
40508     }
40509 });/*
40510  * Based on:
40511  * Ext JS Library 1.1.1
40512  * Copyright(c) 2006-2007, Ext JS, LLC.
40513  *
40514  * Originally Released Under LGPL - original licence link has changed is not relivant.
40515  *
40516  * Fork - LGPL
40517  * <script type="text/javascript">
40518  */
40519  
40520 /**
40521  * @class Roo.form.DateField
40522  * @extends Roo.form.TriggerField
40523  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40524 * @constructor
40525 * Create a new DateField
40526 * @param {Object} config
40527  */
40528 Roo.form.DateField = function(config)
40529 {
40530     Roo.form.DateField.superclass.constructor.call(this, config);
40531     
40532       this.addEvents({
40533          
40534         /**
40535          * @event select
40536          * Fires when a date is selected
40537              * @param {Roo.form.DateField} combo This combo box
40538              * @param {Date} date The date selected
40539              */
40540         'select' : true
40541          
40542     });
40543     
40544     
40545     if(typeof this.minValue == "string") {
40546         this.minValue = this.parseDate(this.minValue);
40547     }
40548     if(typeof this.maxValue == "string") {
40549         this.maxValue = this.parseDate(this.maxValue);
40550     }
40551     this.ddMatch = null;
40552     if(this.disabledDates){
40553         var dd = this.disabledDates;
40554         var re = "(?:";
40555         for(var i = 0; i < dd.length; i++){
40556             re += dd[i];
40557             if(i != dd.length-1) {
40558                 re += "|";
40559             }
40560         }
40561         this.ddMatch = new RegExp(re + ")");
40562     }
40563 };
40564
40565 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40566     /**
40567      * @cfg {String} format
40568      * The default date format string which can be overriden for localization support.  The format must be
40569      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40570      */
40571     format : "m/d/y",
40572     /**
40573      * @cfg {String} altFormats
40574      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40575      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40576      */
40577     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40578     /**
40579      * @cfg {Array} disabledDays
40580      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40581      */
40582     disabledDays : null,
40583     /**
40584      * @cfg {String} disabledDaysText
40585      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40586      */
40587     disabledDaysText : "Disabled",
40588     /**
40589      * @cfg {Array} disabledDates
40590      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40591      * expression so they are very powerful. Some examples:
40592      * <ul>
40593      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40594      * <li>["03/08", "09/16"] would disable those days for every year</li>
40595      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40596      * <li>["03/../2006"] would disable every day in March 2006</li>
40597      * <li>["^03"] would disable every day in every March</li>
40598      * </ul>
40599      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40600      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40601      */
40602     disabledDates : null,
40603     /**
40604      * @cfg {String} disabledDatesText
40605      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40606      */
40607     disabledDatesText : "Disabled",
40608     /**
40609      * @cfg {Date/String} minValue
40610      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40611      * valid format (defaults to null).
40612      */
40613     minValue : null,
40614     /**
40615      * @cfg {Date/String} maxValue
40616      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40617      * valid format (defaults to null).
40618      */
40619     maxValue : null,
40620     /**
40621      * @cfg {String} minText
40622      * The error text to display when the date in the cell is before minValue (defaults to
40623      * 'The date in this field must be after {minValue}').
40624      */
40625     minText : "The date in this field must be equal to or after {0}",
40626     /**
40627      * @cfg {String} maxText
40628      * The error text to display when the date in the cell is after maxValue (defaults to
40629      * 'The date in this field must be before {maxValue}').
40630      */
40631     maxText : "The date in this field must be equal to or before {0}",
40632     /**
40633      * @cfg {String} invalidText
40634      * The error text to display when the date in the field is invalid (defaults to
40635      * '{value} is not a valid date - it must be in the format {format}').
40636      */
40637     invalidText : "{0} is not a valid date - it must be in the format {1}",
40638     /**
40639      * @cfg {String} triggerClass
40640      * An additional CSS class used to style the trigger button.  The trigger will always get the
40641      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40642      * which displays a calendar icon).
40643      */
40644     triggerClass : 'x-form-date-trigger',
40645     
40646
40647     /**
40648      * @cfg {Boolean} useIso
40649      * if enabled, then the date field will use a hidden field to store the 
40650      * real value as iso formated date. default (false)
40651      */ 
40652     useIso : false,
40653     /**
40654      * @cfg {String/Object} autoCreate
40655      * A DomHelper element spec, or true for a default element spec (defaults to
40656      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40657      */ 
40658     // private
40659     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40660     
40661     // private
40662     hiddenField: false,
40663     
40664     onRender : function(ct, position)
40665     {
40666         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40667         if (this.useIso) {
40668             //this.el.dom.removeAttribute('name'); 
40669             Roo.log("Changing name?");
40670             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40671             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40672                     'before', true);
40673             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40674             // prevent input submission
40675             this.hiddenName = this.name;
40676         }
40677             
40678             
40679     },
40680     
40681     // private
40682     validateValue : function(value)
40683     {
40684         value = this.formatDate(value);
40685         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40686             Roo.log('super failed');
40687             return false;
40688         }
40689         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40690              return true;
40691         }
40692         var svalue = value;
40693         value = this.parseDate(value);
40694         if(!value){
40695             Roo.log('parse date failed' + svalue);
40696             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40697             return false;
40698         }
40699         var time = value.getTime();
40700         if(this.minValue && time < this.minValue.getTime()){
40701             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40702             return false;
40703         }
40704         if(this.maxValue && time > this.maxValue.getTime()){
40705             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40706             return false;
40707         }
40708         if(this.disabledDays){
40709             var day = value.getDay();
40710             for(var i = 0; i < this.disabledDays.length; i++) {
40711                 if(day === this.disabledDays[i]){
40712                     this.markInvalid(this.disabledDaysText);
40713                     return false;
40714                 }
40715             }
40716         }
40717         var fvalue = this.formatDate(value);
40718         if(this.ddMatch && this.ddMatch.test(fvalue)){
40719             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40720             return false;
40721         }
40722         return true;
40723     },
40724
40725     // private
40726     // Provides logic to override the default TriggerField.validateBlur which just returns true
40727     validateBlur : function(){
40728         return !this.menu || !this.menu.isVisible();
40729     },
40730     
40731     getName: function()
40732     {
40733         // returns hidden if it's set..
40734         if (!this.rendered) {return ''};
40735         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40736         
40737     },
40738
40739     /**
40740      * Returns the current date value of the date field.
40741      * @return {Date} The date value
40742      */
40743     getValue : function(){
40744         
40745         return  this.hiddenField ?
40746                 this.hiddenField.value :
40747                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40748     },
40749
40750     /**
40751      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40752      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40753      * (the default format used is "m/d/y").
40754      * <br />Usage:
40755      * <pre><code>
40756 //All of these calls set the same date value (May 4, 2006)
40757
40758 //Pass a date object:
40759 var dt = new Date('5/4/06');
40760 dateField.setValue(dt);
40761
40762 //Pass a date string (default format):
40763 dateField.setValue('5/4/06');
40764
40765 //Pass a date string (custom format):
40766 dateField.format = 'Y-m-d';
40767 dateField.setValue('2006-5-4');
40768 </code></pre>
40769      * @param {String/Date} date The date or valid date string
40770      */
40771     setValue : function(date){
40772         if (this.hiddenField) {
40773             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40774         }
40775         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40776         // make sure the value field is always stored as a date..
40777         this.value = this.parseDate(date);
40778         
40779         
40780     },
40781
40782     // private
40783     parseDate : function(value){
40784         if(!value || value instanceof Date){
40785             return value;
40786         }
40787         var v = Date.parseDate(value, this.format);
40788          if (!v && this.useIso) {
40789             v = Date.parseDate(value, 'Y-m-d');
40790         }
40791         if(!v && this.altFormats){
40792             if(!this.altFormatsArray){
40793                 this.altFormatsArray = this.altFormats.split("|");
40794             }
40795             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40796                 v = Date.parseDate(value, this.altFormatsArray[i]);
40797             }
40798         }
40799         return v;
40800     },
40801
40802     // private
40803     formatDate : function(date, fmt){
40804         return (!date || !(date instanceof Date)) ?
40805                date : date.dateFormat(fmt || this.format);
40806     },
40807
40808     // private
40809     menuListeners : {
40810         select: function(m, d){
40811             
40812             this.setValue(d);
40813             this.fireEvent('select', this, d);
40814         },
40815         show : function(){ // retain focus styling
40816             this.onFocus();
40817         },
40818         hide : function(){
40819             this.focus.defer(10, this);
40820             var ml = this.menuListeners;
40821             this.menu.un("select", ml.select,  this);
40822             this.menu.un("show", ml.show,  this);
40823             this.menu.un("hide", ml.hide,  this);
40824         }
40825     },
40826
40827     // private
40828     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40829     onTriggerClick : function(){
40830         if(this.disabled){
40831             return;
40832         }
40833         if(this.menu == null){
40834             this.menu = new Roo.menu.DateMenu();
40835         }
40836         Roo.apply(this.menu.picker,  {
40837             showClear: this.allowBlank,
40838             minDate : this.minValue,
40839             maxDate : this.maxValue,
40840             disabledDatesRE : this.ddMatch,
40841             disabledDatesText : this.disabledDatesText,
40842             disabledDays : this.disabledDays,
40843             disabledDaysText : this.disabledDaysText,
40844             format : this.useIso ? 'Y-m-d' : this.format,
40845             minText : String.format(this.minText, this.formatDate(this.minValue)),
40846             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40847         });
40848         this.menu.on(Roo.apply({}, this.menuListeners, {
40849             scope:this
40850         }));
40851         this.menu.picker.setValue(this.getValue() || new Date());
40852         this.menu.show(this.el, "tl-bl?");
40853     },
40854
40855     beforeBlur : function(){
40856         var v = this.parseDate(this.getRawValue());
40857         if(v){
40858             this.setValue(v);
40859         }
40860     },
40861
40862     /*@
40863      * overide
40864      * 
40865      */
40866     isDirty : function() {
40867         if(this.disabled) {
40868             return false;
40869         }
40870         
40871         if(typeof(this.startValue) === 'undefined'){
40872             return false;
40873         }
40874         
40875         return String(this.getValue()) !== String(this.startValue);
40876         
40877     },
40878     // @overide
40879     cleanLeadingSpace : function(e)
40880     {
40881        return;
40882     }
40883     
40884 });/*
40885  * Based on:
40886  * Ext JS Library 1.1.1
40887  * Copyright(c) 2006-2007, Ext JS, LLC.
40888  *
40889  * Originally Released Under LGPL - original licence link has changed is not relivant.
40890  *
40891  * Fork - LGPL
40892  * <script type="text/javascript">
40893  */
40894  
40895 /**
40896  * @class Roo.form.MonthField
40897  * @extends Roo.form.TriggerField
40898  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40899 * @constructor
40900 * Create a new MonthField
40901 * @param {Object} config
40902  */
40903 Roo.form.MonthField = function(config){
40904     
40905     Roo.form.MonthField.superclass.constructor.call(this, config);
40906     
40907       this.addEvents({
40908          
40909         /**
40910          * @event select
40911          * Fires when a date is selected
40912              * @param {Roo.form.MonthFieeld} combo This combo box
40913              * @param {Date} date The date selected
40914              */
40915         'select' : true
40916          
40917     });
40918     
40919     
40920     if(typeof this.minValue == "string") {
40921         this.minValue = this.parseDate(this.minValue);
40922     }
40923     if(typeof this.maxValue == "string") {
40924         this.maxValue = this.parseDate(this.maxValue);
40925     }
40926     this.ddMatch = null;
40927     if(this.disabledDates){
40928         var dd = this.disabledDates;
40929         var re = "(?:";
40930         for(var i = 0; i < dd.length; i++){
40931             re += dd[i];
40932             if(i != dd.length-1) {
40933                 re += "|";
40934             }
40935         }
40936         this.ddMatch = new RegExp(re + ")");
40937     }
40938 };
40939
40940 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40941     /**
40942      * @cfg {String} format
40943      * The default date format string which can be overriden for localization support.  The format must be
40944      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40945      */
40946     format : "M Y",
40947     /**
40948      * @cfg {String} altFormats
40949      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40950      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40951      */
40952     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40953     /**
40954      * @cfg {Array} disabledDays
40955      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40956      */
40957     disabledDays : [0,1,2,3,4,5,6],
40958     /**
40959      * @cfg {String} disabledDaysText
40960      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40961      */
40962     disabledDaysText : "Disabled",
40963     /**
40964      * @cfg {Array} disabledDates
40965      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40966      * expression so they are very powerful. Some examples:
40967      * <ul>
40968      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40969      * <li>["03/08", "09/16"] would disable those days for every year</li>
40970      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40971      * <li>["03/../2006"] would disable every day in March 2006</li>
40972      * <li>["^03"] would disable every day in every March</li>
40973      * </ul>
40974      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40975      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40976      */
40977     disabledDates : null,
40978     /**
40979      * @cfg {String} disabledDatesText
40980      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40981      */
40982     disabledDatesText : "Disabled",
40983     /**
40984      * @cfg {Date/String} minValue
40985      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40986      * valid format (defaults to null).
40987      */
40988     minValue : null,
40989     /**
40990      * @cfg {Date/String} maxValue
40991      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40992      * valid format (defaults to null).
40993      */
40994     maxValue : null,
40995     /**
40996      * @cfg {String} minText
40997      * The error text to display when the date in the cell is before minValue (defaults to
40998      * 'The date in this field must be after {minValue}').
40999      */
41000     minText : "The date in this field must be equal to or after {0}",
41001     /**
41002      * @cfg {String} maxTextf
41003      * The error text to display when the date in the cell is after maxValue (defaults to
41004      * 'The date in this field must be before {maxValue}').
41005      */
41006     maxText : "The date in this field must be equal to or before {0}",
41007     /**
41008      * @cfg {String} invalidText
41009      * The error text to display when the date in the field is invalid (defaults to
41010      * '{value} is not a valid date - it must be in the format {format}').
41011      */
41012     invalidText : "{0} is not a valid date - it must be in the format {1}",
41013     /**
41014      * @cfg {String} triggerClass
41015      * An additional CSS class used to style the trigger button.  The trigger will always get the
41016      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41017      * which displays a calendar icon).
41018      */
41019     triggerClass : 'x-form-date-trigger',
41020     
41021
41022     /**
41023      * @cfg {Boolean} useIso
41024      * if enabled, then the date field will use a hidden field to store the 
41025      * real value as iso formated date. default (true)
41026      */ 
41027     useIso : true,
41028     /**
41029      * @cfg {String/Object} autoCreate
41030      * A DomHelper element spec, or true for a default element spec (defaults to
41031      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41032      */ 
41033     // private
41034     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41035     
41036     // private
41037     hiddenField: false,
41038     
41039     hideMonthPicker : false,
41040     
41041     onRender : function(ct, position)
41042     {
41043         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41044         if (this.useIso) {
41045             this.el.dom.removeAttribute('name'); 
41046             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41047                     'before', true);
41048             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41049             // prevent input submission
41050             this.hiddenName = this.name;
41051         }
41052             
41053             
41054     },
41055     
41056     // private
41057     validateValue : function(value)
41058     {
41059         value = this.formatDate(value);
41060         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41061             return false;
41062         }
41063         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41064              return true;
41065         }
41066         var svalue = value;
41067         value = this.parseDate(value);
41068         if(!value){
41069             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41070             return false;
41071         }
41072         var time = value.getTime();
41073         if(this.minValue && time < this.minValue.getTime()){
41074             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41075             return false;
41076         }
41077         if(this.maxValue && time > this.maxValue.getTime()){
41078             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41079             return false;
41080         }
41081         /*if(this.disabledDays){
41082             var day = value.getDay();
41083             for(var i = 0; i < this.disabledDays.length; i++) {
41084                 if(day === this.disabledDays[i]){
41085                     this.markInvalid(this.disabledDaysText);
41086                     return false;
41087                 }
41088             }
41089         }
41090         */
41091         var fvalue = this.formatDate(value);
41092         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
41093             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41094             return false;
41095         }
41096         */
41097         return true;
41098     },
41099
41100     // private
41101     // Provides logic to override the default TriggerField.validateBlur which just returns true
41102     validateBlur : function(){
41103         return !this.menu || !this.menu.isVisible();
41104     },
41105
41106     /**
41107      * Returns the current date value of the date field.
41108      * @return {Date} The date value
41109      */
41110     getValue : function(){
41111         
41112         
41113         
41114         return  this.hiddenField ?
41115                 this.hiddenField.value :
41116                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
41117     },
41118
41119     /**
41120      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41121      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
41122      * (the default format used is "m/d/y").
41123      * <br />Usage:
41124      * <pre><code>
41125 //All of these calls set the same date value (May 4, 2006)
41126
41127 //Pass a date object:
41128 var dt = new Date('5/4/06');
41129 monthField.setValue(dt);
41130
41131 //Pass a date string (default format):
41132 monthField.setValue('5/4/06');
41133
41134 //Pass a date string (custom format):
41135 monthField.format = 'Y-m-d';
41136 monthField.setValue('2006-5-4');
41137 </code></pre>
41138      * @param {String/Date} date The date or valid date string
41139      */
41140     setValue : function(date){
41141         Roo.log('month setValue' + date);
41142         // can only be first of month..
41143         
41144         var val = this.parseDate(date);
41145         
41146         if (this.hiddenField) {
41147             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41148         }
41149         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41150         this.value = this.parseDate(date);
41151     },
41152
41153     // private
41154     parseDate : function(value){
41155         if(!value || value instanceof Date){
41156             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
41157             return value;
41158         }
41159         var v = Date.parseDate(value, this.format);
41160         if (!v && this.useIso) {
41161             v = Date.parseDate(value, 'Y-m-d');
41162         }
41163         if (v) {
41164             // 
41165             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
41166         }
41167         
41168         
41169         if(!v && this.altFormats){
41170             if(!this.altFormatsArray){
41171                 this.altFormatsArray = this.altFormats.split("|");
41172             }
41173             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41174                 v = Date.parseDate(value, this.altFormatsArray[i]);
41175             }
41176         }
41177         return v;
41178     },
41179
41180     // private
41181     formatDate : function(date, fmt){
41182         return (!date || !(date instanceof Date)) ?
41183                date : date.dateFormat(fmt || this.format);
41184     },
41185
41186     // private
41187     menuListeners : {
41188         select: function(m, d){
41189             this.setValue(d);
41190             this.fireEvent('select', this, d);
41191         },
41192         show : function(){ // retain focus styling
41193             this.onFocus();
41194         },
41195         hide : function(){
41196             this.focus.defer(10, this);
41197             var ml = this.menuListeners;
41198             this.menu.un("select", ml.select,  this);
41199             this.menu.un("show", ml.show,  this);
41200             this.menu.un("hide", ml.hide,  this);
41201         }
41202     },
41203     // private
41204     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41205     onTriggerClick : function(){
41206         if(this.disabled){
41207             return;
41208         }
41209         if(this.menu == null){
41210             this.menu = new Roo.menu.DateMenu();
41211            
41212         }
41213         
41214         Roo.apply(this.menu.picker,  {
41215             
41216             showClear: this.allowBlank,
41217             minDate : this.minValue,
41218             maxDate : this.maxValue,
41219             disabledDatesRE : this.ddMatch,
41220             disabledDatesText : this.disabledDatesText,
41221             
41222             format : this.useIso ? 'Y-m-d' : this.format,
41223             minText : String.format(this.minText, this.formatDate(this.minValue)),
41224             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41225             
41226         });
41227          this.menu.on(Roo.apply({}, this.menuListeners, {
41228             scope:this
41229         }));
41230        
41231         
41232         var m = this.menu;
41233         var p = m.picker;
41234         
41235         // hide month picker get's called when we called by 'before hide';
41236         
41237         var ignorehide = true;
41238         p.hideMonthPicker  = function(disableAnim){
41239             if (ignorehide) {
41240                 return;
41241             }
41242              if(this.monthPicker){
41243                 Roo.log("hideMonthPicker called");
41244                 if(disableAnim === true){
41245                     this.monthPicker.hide();
41246                 }else{
41247                     this.monthPicker.slideOut('t', {duration:.2});
41248                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41249                     p.fireEvent("select", this, this.value);
41250                     m.hide();
41251                 }
41252             }
41253         }
41254         
41255         Roo.log('picker set value');
41256         Roo.log(this.getValue());
41257         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41258         m.show(this.el, 'tl-bl?');
41259         ignorehide  = false;
41260         // this will trigger hideMonthPicker..
41261         
41262         
41263         // hidden the day picker
41264         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41265         
41266         
41267         
41268       
41269         
41270         p.showMonthPicker.defer(100, p);
41271     
41272         
41273        
41274     },
41275
41276     beforeBlur : function(){
41277         var v = this.parseDate(this.getRawValue());
41278         if(v){
41279             this.setValue(v);
41280         }
41281     }
41282
41283     /** @cfg {Boolean} grow @hide */
41284     /** @cfg {Number} growMin @hide */
41285     /** @cfg {Number} growMax @hide */
41286     /**
41287      * @hide
41288      * @method autoSize
41289      */
41290 });/*
41291  * Based on:
41292  * Ext JS Library 1.1.1
41293  * Copyright(c) 2006-2007, Ext JS, LLC.
41294  *
41295  * Originally Released Under LGPL - original licence link has changed is not relivant.
41296  *
41297  * Fork - LGPL
41298  * <script type="text/javascript">
41299  */
41300  
41301
41302 /**
41303  * @class Roo.form.ComboBox
41304  * @extends Roo.form.TriggerField
41305  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41306  * @constructor
41307  * Create a new ComboBox.
41308  * @param {Object} config Configuration options
41309  */
41310 Roo.form.ComboBox = function(config){
41311     Roo.form.ComboBox.superclass.constructor.call(this, config);
41312     this.addEvents({
41313         /**
41314          * @event expand
41315          * Fires when the dropdown list is expanded
41316              * @param {Roo.form.ComboBox} combo This combo box
41317              */
41318         'expand' : true,
41319         /**
41320          * @event collapse
41321          * Fires when the dropdown list is collapsed
41322              * @param {Roo.form.ComboBox} combo This combo box
41323              */
41324         'collapse' : true,
41325         /**
41326          * @event beforeselect
41327          * Fires before a list item is selected. Return false to cancel the selection.
41328              * @param {Roo.form.ComboBox} combo This combo box
41329              * @param {Roo.data.Record} record The data record returned from the underlying store
41330              * @param {Number} index The index of the selected item in the dropdown list
41331              */
41332         'beforeselect' : true,
41333         /**
41334          * @event select
41335          * Fires when a list item is selected
41336              * @param {Roo.form.ComboBox} combo This combo box
41337              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41338              * @param {Number} index The index of the selected item in the dropdown list
41339              */
41340         'select' : true,
41341         /**
41342          * @event beforequery
41343          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41344          * The event object passed has these properties:
41345              * @param {Roo.form.ComboBox} combo This combo box
41346              * @param {String} query The query
41347              * @param {Boolean} forceAll true to force "all" query
41348              * @param {Boolean} cancel true to cancel the query
41349              * @param {Object} e The query event object
41350              */
41351         'beforequery': true,
41352          /**
41353          * @event add
41354          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41355              * @param {Roo.form.ComboBox} combo This combo box
41356              */
41357         'add' : true,
41358         /**
41359          * @event edit
41360          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41361              * @param {Roo.form.ComboBox} combo This combo box
41362              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41363              */
41364         'edit' : true
41365         
41366         
41367     });
41368     if(this.transform){
41369         this.allowDomMove = false;
41370         var s = Roo.getDom(this.transform);
41371         if(!this.hiddenName){
41372             this.hiddenName = s.name;
41373         }
41374         if(!this.store){
41375             this.mode = 'local';
41376             var d = [], opts = s.options;
41377             for(var i = 0, len = opts.length;i < len; i++){
41378                 var o = opts[i];
41379                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41380                 if(o.selected) {
41381                     this.value = value;
41382                 }
41383                 d.push([value, o.text]);
41384             }
41385             this.store = new Roo.data.SimpleStore({
41386                 'id': 0,
41387                 fields: ['value', 'text'],
41388                 data : d
41389             });
41390             this.valueField = 'value';
41391             this.displayField = 'text';
41392         }
41393         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41394         if(!this.lazyRender){
41395             this.target = true;
41396             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41397             s.parentNode.removeChild(s); // remove it
41398             this.render(this.el.parentNode);
41399         }else{
41400             s.parentNode.removeChild(s); // remove it
41401         }
41402
41403     }
41404     if (this.store) {
41405         this.store = Roo.factory(this.store, Roo.data);
41406     }
41407     
41408     this.selectedIndex = -1;
41409     if(this.mode == 'local'){
41410         if(config.queryDelay === undefined){
41411             this.queryDelay = 10;
41412         }
41413         if(config.minChars === undefined){
41414             this.minChars = 0;
41415         }
41416     }
41417 };
41418
41419 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41420     /**
41421      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41422      */
41423     /**
41424      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41425      * rendering into an Roo.Editor, defaults to false)
41426      */
41427     /**
41428      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41429      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41430      */
41431     /**
41432      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41433      */
41434     /**
41435      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41436      * the dropdown list (defaults to undefined, with no header element)
41437      */
41438
41439      /**
41440      * @cfg {String/Roo.Template} tpl The template to use to render the output
41441      */
41442      
41443     // private
41444     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41445     /**
41446      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41447      */
41448     listWidth: undefined,
41449     /**
41450      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41451      * mode = 'remote' or 'text' if mode = 'local')
41452      */
41453     displayField: undefined,
41454     /**
41455      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41456      * mode = 'remote' or 'value' if mode = 'local'). 
41457      * Note: use of a valueField requires the user make a selection
41458      * in order for a value to be mapped.
41459      */
41460     valueField: undefined,
41461     
41462     
41463     /**
41464      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41465      * field's data value (defaults to the underlying DOM element's name)
41466      */
41467     hiddenName: undefined,
41468     /**
41469      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41470      */
41471     listClass: '',
41472     /**
41473      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41474      */
41475     selectedClass: 'x-combo-selected',
41476     /**
41477      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41478      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41479      * which displays a downward arrow icon).
41480      */
41481     triggerClass : 'x-form-arrow-trigger',
41482     /**
41483      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41484      */
41485     shadow:'sides',
41486     /**
41487      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41488      * anchor positions (defaults to 'tl-bl')
41489      */
41490     listAlign: 'tl-bl?',
41491     /**
41492      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41493      */
41494     maxHeight: 300,
41495     /**
41496      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41497      * query specified by the allQuery config option (defaults to 'query')
41498      */
41499     triggerAction: 'query',
41500     /**
41501      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41502      * (defaults to 4, does not apply if editable = false)
41503      */
41504     minChars : 4,
41505     /**
41506      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41507      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41508      */
41509     typeAhead: false,
41510     /**
41511      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41512      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41513      */
41514     queryDelay: 500,
41515     /**
41516      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41517      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41518      */
41519     pageSize: 0,
41520     /**
41521      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41522      * when editable = true (defaults to false)
41523      */
41524     selectOnFocus:false,
41525     /**
41526      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41527      */
41528     queryParam: 'query',
41529     /**
41530      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41531      * when mode = 'remote' (defaults to 'Loading...')
41532      */
41533     loadingText: 'Loading...',
41534     /**
41535      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41536      */
41537     resizable: false,
41538     /**
41539      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41540      */
41541     handleHeight : 8,
41542     /**
41543      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41544      * traditional select (defaults to true)
41545      */
41546     editable: true,
41547     /**
41548      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41549      */
41550     allQuery: '',
41551     /**
41552      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41553      */
41554     mode: 'remote',
41555     /**
41556      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41557      * listWidth has a higher value)
41558      */
41559     minListWidth : 70,
41560     /**
41561      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41562      * allow the user to set arbitrary text into the field (defaults to false)
41563      */
41564     forceSelection:false,
41565     /**
41566      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41567      * if typeAhead = true (defaults to 250)
41568      */
41569     typeAheadDelay : 250,
41570     /**
41571      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41572      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41573      */
41574     valueNotFoundText : undefined,
41575     /**
41576      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41577      */
41578     blockFocus : false,
41579     
41580     /**
41581      * @cfg {Boolean} disableClear Disable showing of clear button.
41582      */
41583     disableClear : false,
41584     /**
41585      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41586      */
41587     alwaysQuery : false,
41588     
41589     //private
41590     addicon : false,
41591     editicon: false,
41592     
41593     // element that contains real text value.. (when hidden is used..)
41594      
41595     // private
41596     onRender : function(ct, position)
41597     {
41598         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41599         
41600         if(this.hiddenName){
41601             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41602                     'before', true);
41603             this.hiddenField.value =
41604                 this.hiddenValue !== undefined ? this.hiddenValue :
41605                 this.value !== undefined ? this.value : '';
41606
41607             // prevent input submission
41608             this.el.dom.removeAttribute('name');
41609              
41610              
41611         }
41612         
41613         if(Roo.isGecko){
41614             this.el.dom.setAttribute('autocomplete', 'off');
41615         }
41616
41617         var cls = 'x-combo-list';
41618
41619         this.list = new Roo.Layer({
41620             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41621         });
41622
41623         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41624         this.list.setWidth(lw);
41625         this.list.swallowEvent('mousewheel');
41626         this.assetHeight = 0;
41627
41628         if(this.title){
41629             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41630             this.assetHeight += this.header.getHeight();
41631         }
41632
41633         this.innerList = this.list.createChild({cls:cls+'-inner'});
41634         this.innerList.on('mouseover', this.onViewOver, this);
41635         this.innerList.on('mousemove', this.onViewMove, this);
41636         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41637         
41638         if(this.allowBlank && !this.pageSize && !this.disableClear){
41639             this.footer = this.list.createChild({cls:cls+'-ft'});
41640             this.pageTb = new Roo.Toolbar(this.footer);
41641            
41642         }
41643         if(this.pageSize){
41644             this.footer = this.list.createChild({cls:cls+'-ft'});
41645             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41646                     {pageSize: this.pageSize});
41647             
41648         }
41649         
41650         if (this.pageTb && this.allowBlank && !this.disableClear) {
41651             var _this = this;
41652             this.pageTb.add(new Roo.Toolbar.Fill(), {
41653                 cls: 'x-btn-icon x-btn-clear',
41654                 text: '&#160;',
41655                 handler: function()
41656                 {
41657                     _this.collapse();
41658                     _this.clearValue();
41659                     _this.onSelect(false, -1);
41660                 }
41661             });
41662         }
41663         if (this.footer) {
41664             this.assetHeight += this.footer.getHeight();
41665         }
41666         
41667
41668         if(!this.tpl){
41669             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41670         }
41671
41672         this.view = new Roo.View(this.innerList, this.tpl, {
41673             singleSelect:true,
41674             store: this.store,
41675             selectedClass: this.selectedClass
41676         });
41677
41678         this.view.on('click', this.onViewClick, this);
41679
41680         this.store.on('beforeload', this.onBeforeLoad, this);
41681         this.store.on('load', this.onLoad, this);
41682         this.store.on('loadexception', this.onLoadException, this);
41683
41684         if(this.resizable){
41685             this.resizer = new Roo.Resizable(this.list,  {
41686                pinned:true, handles:'se'
41687             });
41688             this.resizer.on('resize', function(r, w, h){
41689                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41690                 this.listWidth = w;
41691                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41692                 this.restrictHeight();
41693             }, this);
41694             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41695         }
41696         if(!this.editable){
41697             this.editable = true;
41698             this.setEditable(false);
41699         }  
41700         
41701         
41702         if (typeof(this.events.add.listeners) != 'undefined') {
41703             
41704             this.addicon = this.wrap.createChild(
41705                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41706        
41707             this.addicon.on('click', function(e) {
41708                 this.fireEvent('add', this);
41709             }, this);
41710         }
41711         if (typeof(this.events.edit.listeners) != 'undefined') {
41712             
41713             this.editicon = this.wrap.createChild(
41714                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41715             if (this.addicon) {
41716                 this.editicon.setStyle('margin-left', '40px');
41717             }
41718             this.editicon.on('click', function(e) {
41719                 
41720                 // we fire even  if inothing is selected..
41721                 this.fireEvent('edit', this, this.lastData );
41722                 
41723             }, this);
41724         }
41725         
41726         
41727         
41728     },
41729
41730     // private
41731     initEvents : function(){
41732         Roo.form.ComboBox.superclass.initEvents.call(this);
41733
41734         this.keyNav = new Roo.KeyNav(this.el, {
41735             "up" : function(e){
41736                 this.inKeyMode = true;
41737                 this.selectPrev();
41738             },
41739
41740             "down" : function(e){
41741                 if(!this.isExpanded()){
41742                     this.onTriggerClick();
41743                 }else{
41744                     this.inKeyMode = true;
41745                     this.selectNext();
41746                 }
41747             },
41748
41749             "enter" : function(e){
41750                 this.onViewClick();
41751                 //return true;
41752             },
41753
41754             "esc" : function(e){
41755                 this.collapse();
41756             },
41757
41758             "tab" : function(e){
41759                 this.onViewClick(false);
41760                 this.fireEvent("specialkey", this, e);
41761                 return true;
41762             },
41763
41764             scope : this,
41765
41766             doRelay : function(foo, bar, hname){
41767                 if(hname == 'down' || this.scope.isExpanded()){
41768                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41769                 }
41770                 return true;
41771             },
41772
41773             forceKeyDown: true
41774         });
41775         this.queryDelay = Math.max(this.queryDelay || 10,
41776                 this.mode == 'local' ? 10 : 250);
41777         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41778         if(this.typeAhead){
41779             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41780         }
41781         if(this.editable !== false){
41782             this.el.on("keyup", this.onKeyUp, this);
41783         }
41784         if(this.forceSelection){
41785             this.on('blur', this.doForce, this);
41786         }
41787     },
41788
41789     onDestroy : function(){
41790         if(this.view){
41791             this.view.setStore(null);
41792             this.view.el.removeAllListeners();
41793             this.view.el.remove();
41794             this.view.purgeListeners();
41795         }
41796         if(this.list){
41797             this.list.destroy();
41798         }
41799         if(this.store){
41800             this.store.un('beforeload', this.onBeforeLoad, this);
41801             this.store.un('load', this.onLoad, this);
41802             this.store.un('loadexception', this.onLoadException, this);
41803         }
41804         Roo.form.ComboBox.superclass.onDestroy.call(this);
41805     },
41806
41807     // private
41808     fireKey : function(e){
41809         if(e.isNavKeyPress() && !this.list.isVisible()){
41810             this.fireEvent("specialkey", this, e);
41811         }
41812     },
41813
41814     // private
41815     onResize: function(w, h){
41816         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41817         
41818         if(typeof w != 'number'){
41819             // we do not handle it!?!?
41820             return;
41821         }
41822         var tw = this.trigger.getWidth();
41823         tw += this.addicon ? this.addicon.getWidth() : 0;
41824         tw += this.editicon ? this.editicon.getWidth() : 0;
41825         var x = w - tw;
41826         this.el.setWidth( this.adjustWidth('input', x));
41827             
41828         this.trigger.setStyle('left', x+'px');
41829         
41830         if(this.list && this.listWidth === undefined){
41831             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41832             this.list.setWidth(lw);
41833             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41834         }
41835         
41836     
41837         
41838     },
41839
41840     /**
41841      * Allow or prevent the user from directly editing the field text.  If false is passed,
41842      * the user will only be able to select from the items defined in the dropdown list.  This method
41843      * is the runtime equivalent of setting the 'editable' config option at config time.
41844      * @param {Boolean} value True to allow the user to directly edit the field text
41845      */
41846     setEditable : function(value){
41847         if(value == this.editable){
41848             return;
41849         }
41850         this.editable = value;
41851         if(!value){
41852             this.el.dom.setAttribute('readOnly', true);
41853             this.el.on('mousedown', this.onTriggerClick,  this);
41854             this.el.addClass('x-combo-noedit');
41855         }else{
41856             this.el.dom.setAttribute('readOnly', false);
41857             this.el.un('mousedown', this.onTriggerClick,  this);
41858             this.el.removeClass('x-combo-noedit');
41859         }
41860     },
41861
41862     // private
41863     onBeforeLoad : function(){
41864         if(!this.hasFocus){
41865             return;
41866         }
41867         this.innerList.update(this.loadingText ?
41868                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41869         this.restrictHeight();
41870         this.selectedIndex = -1;
41871     },
41872
41873     // private
41874     onLoad : function(){
41875         if(!this.hasFocus){
41876             return;
41877         }
41878         if(this.store.getCount() > 0){
41879             this.expand();
41880             this.restrictHeight();
41881             if(this.lastQuery == this.allQuery){
41882                 if(this.editable){
41883                     this.el.dom.select();
41884                 }
41885                 if(!this.selectByValue(this.value, true)){
41886                     this.select(0, true);
41887                 }
41888             }else{
41889                 this.selectNext();
41890                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41891                     this.taTask.delay(this.typeAheadDelay);
41892                 }
41893             }
41894         }else{
41895             this.onEmptyResults();
41896         }
41897         //this.el.focus();
41898     },
41899     // private
41900     onLoadException : function()
41901     {
41902         this.collapse();
41903         Roo.log(this.store.reader.jsonData);
41904         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41905             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41906         }
41907         
41908         
41909     },
41910     // private
41911     onTypeAhead : function(){
41912         if(this.store.getCount() > 0){
41913             var r = this.store.getAt(0);
41914             var newValue = r.data[this.displayField];
41915             var len = newValue.length;
41916             var selStart = this.getRawValue().length;
41917             if(selStart != len){
41918                 this.setRawValue(newValue);
41919                 this.selectText(selStart, newValue.length);
41920             }
41921         }
41922     },
41923
41924     // private
41925     onSelect : function(record, index){
41926         if(this.fireEvent('beforeselect', this, record, index) !== false){
41927             this.setFromData(index > -1 ? record.data : false);
41928             this.collapse();
41929             this.fireEvent('select', this, record, index);
41930         }
41931     },
41932
41933     /**
41934      * Returns the currently selected field value or empty string if no value is set.
41935      * @return {String} value The selected value
41936      */
41937     getValue : function(){
41938         if(this.valueField){
41939             return typeof this.value != 'undefined' ? this.value : '';
41940         }
41941         return Roo.form.ComboBox.superclass.getValue.call(this);
41942     },
41943
41944     /**
41945      * Clears any text/value currently set in the field
41946      */
41947     clearValue : function(){
41948         if(this.hiddenField){
41949             this.hiddenField.value = '';
41950         }
41951         this.value = '';
41952         this.setRawValue('');
41953         this.lastSelectionText = '';
41954         
41955     },
41956
41957     /**
41958      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41959      * will be displayed in the field.  If the value does not match the data value of an existing item,
41960      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41961      * Otherwise the field will be blank (although the value will still be set).
41962      * @param {String} value The value to match
41963      */
41964     setValue : function(v){
41965         var text = v;
41966         if(this.valueField){
41967             var r = this.findRecord(this.valueField, v);
41968             if(r){
41969                 text = r.data[this.displayField];
41970             }else if(this.valueNotFoundText !== undefined){
41971                 text = this.valueNotFoundText;
41972             }
41973         }
41974         this.lastSelectionText = text;
41975         if(this.hiddenField){
41976             this.hiddenField.value = v;
41977         }
41978         Roo.form.ComboBox.superclass.setValue.call(this, text);
41979         this.value = v;
41980     },
41981     /**
41982      * @property {Object} the last set data for the element
41983      */
41984     
41985     lastData : false,
41986     /**
41987      * Sets the value of the field based on a object which is related to the record format for the store.
41988      * @param {Object} value the value to set as. or false on reset?
41989      */
41990     setFromData : function(o){
41991         var dv = ''; // display value
41992         var vv = ''; // value value..
41993         this.lastData = o;
41994         if (this.displayField) {
41995             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41996         } else {
41997             // this is an error condition!!!
41998             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41999         }
42000         
42001         if(this.valueField){
42002             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42003         }
42004         if(this.hiddenField){
42005             this.hiddenField.value = vv;
42006             
42007             this.lastSelectionText = dv;
42008             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42009             this.value = vv;
42010             return;
42011         }
42012         // no hidden field.. - we store the value in 'value', but still display
42013         // display field!!!!
42014         this.lastSelectionText = dv;
42015         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42016         this.value = vv;
42017         
42018         
42019     },
42020     // private
42021     reset : function(){
42022         // overridden so that last data is reset..
42023         this.setValue(this.resetValue);
42024         this.originalValue = this.getValue();
42025         this.clearInvalid();
42026         this.lastData = false;
42027         if (this.view) {
42028             this.view.clearSelections();
42029         }
42030     },
42031     // private
42032     findRecord : function(prop, value){
42033         var record;
42034         if(this.store.getCount() > 0){
42035             this.store.each(function(r){
42036                 if(r.data[prop] == value){
42037                     record = r;
42038                     return false;
42039                 }
42040                 return true;
42041             });
42042         }
42043         return record;
42044     },
42045     
42046     getName: function()
42047     {
42048         // returns hidden if it's set..
42049         if (!this.rendered) {return ''};
42050         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42051         
42052     },
42053     // private
42054     onViewMove : function(e, t){
42055         this.inKeyMode = false;
42056     },
42057
42058     // private
42059     onViewOver : function(e, t){
42060         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42061             return;
42062         }
42063         var item = this.view.findItemFromChild(t);
42064         if(item){
42065             var index = this.view.indexOf(item);
42066             this.select(index, false);
42067         }
42068     },
42069
42070     // private
42071     onViewClick : function(doFocus)
42072     {
42073         var index = this.view.getSelectedIndexes()[0];
42074         var r = this.store.getAt(index);
42075         if(r){
42076             this.onSelect(r, index);
42077         }
42078         if(doFocus !== false && !this.blockFocus){
42079             this.el.focus();
42080         }
42081     },
42082
42083     // private
42084     restrictHeight : function(){
42085         this.innerList.dom.style.height = '';
42086         var inner = this.innerList.dom;
42087         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42088         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42089         this.list.beginUpdate();
42090         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
42091         this.list.alignTo(this.el, this.listAlign);
42092         this.list.endUpdate();
42093     },
42094
42095     // private
42096     onEmptyResults : function(){
42097         this.collapse();
42098     },
42099
42100     /**
42101      * Returns true if the dropdown list is expanded, else false.
42102      */
42103     isExpanded : function(){
42104         return this.list.isVisible();
42105     },
42106
42107     /**
42108      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
42109      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42110      * @param {String} value The data value of the item to select
42111      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42112      * selected item if it is not currently in view (defaults to true)
42113      * @return {Boolean} True if the value matched an item in the list, else false
42114      */
42115     selectByValue : function(v, scrollIntoView){
42116         if(v !== undefined && v !== null){
42117             var r = this.findRecord(this.valueField || this.displayField, v);
42118             if(r){
42119                 this.select(this.store.indexOf(r), scrollIntoView);
42120                 return true;
42121             }
42122         }
42123         return false;
42124     },
42125
42126     /**
42127      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
42128      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42129      * @param {Number} index The zero-based index of the list item to select
42130      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42131      * selected item if it is not currently in view (defaults to true)
42132      */
42133     select : function(index, scrollIntoView){
42134         this.selectedIndex = index;
42135         this.view.select(index);
42136         if(scrollIntoView !== false){
42137             var el = this.view.getNode(index);
42138             if(el){
42139                 this.innerList.scrollChildIntoView(el, false);
42140             }
42141         }
42142     },
42143
42144     // private
42145     selectNext : function(){
42146         var ct = this.store.getCount();
42147         if(ct > 0){
42148             if(this.selectedIndex == -1){
42149                 this.select(0);
42150             }else if(this.selectedIndex < ct-1){
42151                 this.select(this.selectedIndex+1);
42152             }
42153         }
42154     },
42155
42156     // private
42157     selectPrev : function(){
42158         var ct = this.store.getCount();
42159         if(ct > 0){
42160             if(this.selectedIndex == -1){
42161                 this.select(0);
42162             }else if(this.selectedIndex != 0){
42163                 this.select(this.selectedIndex-1);
42164             }
42165         }
42166     },
42167
42168     // private
42169     onKeyUp : function(e){
42170         if(this.editable !== false && !e.isSpecialKey()){
42171             this.lastKey = e.getKey();
42172             this.dqTask.delay(this.queryDelay);
42173         }
42174     },
42175
42176     // private
42177     validateBlur : function(){
42178         return !this.list || !this.list.isVisible();   
42179     },
42180
42181     // private
42182     initQuery : function(){
42183         this.doQuery(this.getRawValue());
42184     },
42185
42186     // private
42187     doForce : function(){
42188         if(this.el.dom.value.length > 0){
42189             this.el.dom.value =
42190                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
42191              
42192         }
42193     },
42194
42195     /**
42196      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
42197      * query allowing the query action to be canceled if needed.
42198      * @param {String} query The SQL query to execute
42199      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
42200      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
42201      * saved in the current store (defaults to false)
42202      */
42203     doQuery : function(q, forceAll){
42204         if(q === undefined || q === null){
42205             q = '';
42206         }
42207         var qe = {
42208             query: q,
42209             forceAll: forceAll,
42210             combo: this,
42211             cancel:false
42212         };
42213         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42214             return false;
42215         }
42216         q = qe.query;
42217         forceAll = qe.forceAll;
42218         if(forceAll === true || (q.length >= this.minChars)){
42219             if(this.lastQuery != q || this.alwaysQuery){
42220                 this.lastQuery = q;
42221                 if(this.mode == 'local'){
42222                     this.selectedIndex = -1;
42223                     if(forceAll){
42224                         this.store.clearFilter();
42225                     }else{
42226                         this.store.filter(this.displayField, q);
42227                     }
42228                     this.onLoad();
42229                 }else{
42230                     this.store.baseParams[this.queryParam] = q;
42231                     this.store.load({
42232                         params: this.getParams(q)
42233                     });
42234                     this.expand();
42235                 }
42236             }else{
42237                 this.selectedIndex = -1;
42238                 this.onLoad();   
42239             }
42240         }
42241     },
42242
42243     // private
42244     getParams : function(q){
42245         var p = {};
42246         //p[this.queryParam] = q;
42247         if(this.pageSize){
42248             p.start = 0;
42249             p.limit = this.pageSize;
42250         }
42251         return p;
42252     },
42253
42254     /**
42255      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42256      */
42257     collapse : function(){
42258         if(!this.isExpanded()){
42259             return;
42260         }
42261         this.list.hide();
42262         Roo.get(document).un('mousedown', this.collapseIf, this);
42263         Roo.get(document).un('mousewheel', this.collapseIf, this);
42264         if (!this.editable) {
42265             Roo.get(document).un('keydown', this.listKeyPress, this);
42266         }
42267         this.fireEvent('collapse', this);
42268     },
42269
42270     // private
42271     collapseIf : function(e){
42272         if(!e.within(this.wrap) && !e.within(this.list)){
42273             this.collapse();
42274         }
42275     },
42276
42277     /**
42278      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42279      */
42280     expand : function(){
42281         if(this.isExpanded() || !this.hasFocus){
42282             return;
42283         }
42284         this.list.alignTo(this.el, this.listAlign);
42285         this.list.show();
42286         Roo.get(document).on('mousedown', this.collapseIf, this);
42287         Roo.get(document).on('mousewheel', this.collapseIf, this);
42288         if (!this.editable) {
42289             Roo.get(document).on('keydown', this.listKeyPress, this);
42290         }
42291         
42292         this.fireEvent('expand', this);
42293     },
42294
42295     // private
42296     // Implements the default empty TriggerField.onTriggerClick function
42297     onTriggerClick : function(){
42298         if(this.disabled){
42299             return;
42300         }
42301         if(this.isExpanded()){
42302             this.collapse();
42303             if (!this.blockFocus) {
42304                 this.el.focus();
42305             }
42306             
42307         }else {
42308             this.hasFocus = true;
42309             if(this.triggerAction == 'all') {
42310                 this.doQuery(this.allQuery, true);
42311             } else {
42312                 this.doQuery(this.getRawValue());
42313             }
42314             if (!this.blockFocus) {
42315                 this.el.focus();
42316             }
42317         }
42318     },
42319     listKeyPress : function(e)
42320     {
42321         //Roo.log('listkeypress');
42322         // scroll to first matching element based on key pres..
42323         if (e.isSpecialKey()) {
42324             return false;
42325         }
42326         var k = String.fromCharCode(e.getKey()).toUpperCase();
42327         //Roo.log(k);
42328         var match  = false;
42329         var csel = this.view.getSelectedNodes();
42330         var cselitem = false;
42331         if (csel.length) {
42332             var ix = this.view.indexOf(csel[0]);
42333             cselitem  = this.store.getAt(ix);
42334             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42335                 cselitem = false;
42336             }
42337             
42338         }
42339         
42340         this.store.each(function(v) { 
42341             if (cselitem) {
42342                 // start at existing selection.
42343                 if (cselitem.id == v.id) {
42344                     cselitem = false;
42345                 }
42346                 return;
42347             }
42348                 
42349             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42350                 match = this.store.indexOf(v);
42351                 return false;
42352             }
42353         }, this);
42354         
42355         if (match === false) {
42356             return true; // no more action?
42357         }
42358         // scroll to?
42359         this.view.select(match);
42360         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42361         sn.scrollIntoView(sn.dom.parentNode, false);
42362     } 
42363
42364     /** 
42365     * @cfg {Boolean} grow 
42366     * @hide 
42367     */
42368     /** 
42369     * @cfg {Number} growMin 
42370     * @hide 
42371     */
42372     /** 
42373     * @cfg {Number} growMax 
42374     * @hide 
42375     */
42376     /**
42377      * @hide
42378      * @method autoSize
42379      */
42380 });/*
42381  * Copyright(c) 2010-2012, Roo J Solutions Limited
42382  *
42383  * Licence LGPL
42384  *
42385  */
42386
42387 /**
42388  * @class Roo.form.ComboBoxArray
42389  * @extends Roo.form.TextField
42390  * A facebook style adder... for lists of email / people / countries  etc...
42391  * pick multiple items from a combo box, and shows each one.
42392  *
42393  *  Fred [x]  Brian [x]  [Pick another |v]
42394  *
42395  *
42396  *  For this to work: it needs various extra information
42397  *    - normal combo problay has
42398  *      name, hiddenName
42399  *    + displayField, valueField
42400  *
42401  *    For our purpose...
42402  *
42403  *
42404  *   If we change from 'extends' to wrapping...
42405  *   
42406  *  
42407  *
42408  
42409  
42410  * @constructor
42411  * Create a new ComboBoxArray.
42412  * @param {Object} config Configuration options
42413  */
42414  
42415
42416 Roo.form.ComboBoxArray = function(config)
42417 {
42418     this.addEvents({
42419         /**
42420          * @event beforeremove
42421          * Fires before remove the value from the list
42422              * @param {Roo.form.ComboBoxArray} _self This combo box array
42423              * @param {Roo.form.ComboBoxArray.Item} item removed item
42424              */
42425         'beforeremove' : true,
42426         /**
42427          * @event remove
42428          * Fires when remove the value from the list
42429              * @param {Roo.form.ComboBoxArray} _self This combo box array
42430              * @param {Roo.form.ComboBoxArray.Item} item removed item
42431              */
42432         'remove' : true
42433         
42434         
42435     });
42436     
42437     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42438     
42439     this.items = new Roo.util.MixedCollection(false);
42440     
42441     // construct the child combo...
42442     
42443     
42444     
42445     
42446    
42447     
42448 }
42449
42450  
42451 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42452
42453     /**
42454      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42455      */
42456     
42457     lastData : false,
42458     
42459     // behavies liek a hiddne field
42460     inputType:      'hidden',
42461     /**
42462      * @cfg {Number} width The width of the box that displays the selected element
42463      */ 
42464     width:          300,
42465
42466     
42467     
42468     /**
42469      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42470      */
42471     name : false,
42472     /**
42473      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42474      */
42475     hiddenName : false,
42476       /**
42477      * @cfg {String} seperator    The value seperator normally ',' 
42478      */
42479     seperator : ',',
42480     
42481     // private the array of items that are displayed..
42482     items  : false,
42483     // private - the hidden field el.
42484     hiddenEl : false,
42485     // private - the filed el..
42486     el : false,
42487     
42488     //validateValue : function() { return true; }, // all values are ok!
42489     //onAddClick: function() { },
42490     
42491     onRender : function(ct, position) 
42492     {
42493         
42494         // create the standard hidden element
42495         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42496         
42497         
42498         // give fake names to child combo;
42499         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42500         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42501         
42502         this.combo = Roo.factory(this.combo, Roo.form);
42503         this.combo.onRender(ct, position);
42504         if (typeof(this.combo.width) != 'undefined') {
42505             this.combo.onResize(this.combo.width,0);
42506         }
42507         
42508         this.combo.initEvents();
42509         
42510         // assigned so form know we need to do this..
42511         this.store          = this.combo.store;
42512         this.valueField     = this.combo.valueField;
42513         this.displayField   = this.combo.displayField ;
42514         
42515         
42516         this.combo.wrap.addClass('x-cbarray-grp');
42517         
42518         var cbwrap = this.combo.wrap.createChild(
42519             {tag: 'div', cls: 'x-cbarray-cb'},
42520             this.combo.el.dom
42521         );
42522         
42523              
42524         this.hiddenEl = this.combo.wrap.createChild({
42525             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42526         });
42527         this.el = this.combo.wrap.createChild({
42528             tag: 'input',  type:'hidden' , name: this.name, value : ''
42529         });
42530          //   this.el.dom.removeAttribute("name");
42531         
42532         
42533         this.outerWrap = this.combo.wrap;
42534         this.wrap = cbwrap;
42535         
42536         this.outerWrap.setWidth(this.width);
42537         this.outerWrap.dom.removeChild(this.el.dom);
42538         
42539         this.wrap.dom.appendChild(this.el.dom);
42540         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42541         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42542         
42543         this.combo.trigger.setStyle('position','relative');
42544         this.combo.trigger.setStyle('left', '0px');
42545         this.combo.trigger.setStyle('top', '2px');
42546         
42547         this.combo.el.setStyle('vertical-align', 'text-bottom');
42548         
42549         //this.trigger.setStyle('vertical-align', 'top');
42550         
42551         // this should use the code from combo really... on('add' ....)
42552         if (this.adder) {
42553             
42554         
42555             this.adder = this.outerWrap.createChild(
42556                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42557             var _t = this;
42558             this.adder.on('click', function(e) {
42559                 _t.fireEvent('adderclick', this, e);
42560             }, _t);
42561         }
42562         //var _t = this;
42563         //this.adder.on('click', this.onAddClick, _t);
42564         
42565         
42566         this.combo.on('select', function(cb, rec, ix) {
42567             this.addItem(rec.data);
42568             
42569             cb.setValue('');
42570             cb.el.dom.value = '';
42571             //cb.lastData = rec.data;
42572             // add to list
42573             
42574         }, this);
42575         
42576         
42577     },
42578     
42579     
42580     getName: function()
42581     {
42582         // returns hidden if it's set..
42583         if (!this.rendered) {return ''};
42584         return  this.hiddenName ? this.hiddenName : this.name;
42585         
42586     },
42587     
42588     
42589     onResize: function(w, h){
42590         
42591         return;
42592         // not sure if this is needed..
42593         //this.combo.onResize(w,h);
42594         
42595         if(typeof w != 'number'){
42596             // we do not handle it!?!?
42597             return;
42598         }
42599         var tw = this.combo.trigger.getWidth();
42600         tw += this.addicon ? this.addicon.getWidth() : 0;
42601         tw += this.editicon ? this.editicon.getWidth() : 0;
42602         var x = w - tw;
42603         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42604             
42605         this.combo.trigger.setStyle('left', '0px');
42606         
42607         if(this.list && this.listWidth === undefined){
42608             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42609             this.list.setWidth(lw);
42610             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42611         }
42612         
42613     
42614         
42615     },
42616     
42617     addItem: function(rec)
42618     {
42619         var valueField = this.combo.valueField;
42620         var displayField = this.combo.displayField;
42621         
42622         if (this.items.indexOfKey(rec[valueField]) > -1) {
42623             //console.log("GOT " + rec.data.id);
42624             return;
42625         }
42626         
42627         var x = new Roo.form.ComboBoxArray.Item({
42628             //id : rec[this.idField],
42629             data : rec,
42630             displayField : displayField ,
42631             tipField : displayField ,
42632             cb : this
42633         });
42634         // use the 
42635         this.items.add(rec[valueField],x);
42636         // add it before the element..
42637         this.updateHiddenEl();
42638         x.render(this.outerWrap, this.wrap.dom);
42639         // add the image handler..
42640     },
42641     
42642     updateHiddenEl : function()
42643     {
42644         this.validate();
42645         if (!this.hiddenEl) {
42646             return;
42647         }
42648         var ar = [];
42649         var idField = this.combo.valueField;
42650         
42651         this.items.each(function(f) {
42652             ar.push(f.data[idField]);
42653         });
42654         this.hiddenEl.dom.value = ar.join(this.seperator);
42655         this.validate();
42656     },
42657     
42658     reset : function()
42659     {
42660         this.items.clear();
42661         
42662         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42663            el.remove();
42664         });
42665         
42666         this.el.dom.value = '';
42667         if (this.hiddenEl) {
42668             this.hiddenEl.dom.value = '';
42669         }
42670         
42671     },
42672     getValue: function()
42673     {
42674         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42675     },
42676     setValue: function(v) // not a valid action - must use addItems..
42677     {
42678         
42679         this.reset();
42680          
42681         if (this.store.isLocal && (typeof(v) == 'string')) {
42682             // then we can use the store to find the values..
42683             // comma seperated at present.. this needs to allow JSON based encoding..
42684             this.hiddenEl.value  = v;
42685             var v_ar = [];
42686             Roo.each(v.split(this.seperator), function(k) {
42687                 Roo.log("CHECK " + this.valueField + ',' + k);
42688                 var li = this.store.query(this.valueField, k);
42689                 if (!li.length) {
42690                     return;
42691                 }
42692                 var add = {};
42693                 add[this.valueField] = k;
42694                 add[this.displayField] = li.item(0).data[this.displayField];
42695                 
42696                 this.addItem(add);
42697             }, this) 
42698              
42699         }
42700         if (typeof(v) == 'object' ) {
42701             // then let's assume it's an array of objects..
42702             Roo.each(v, function(l) {
42703                 var add = l;
42704                 if (typeof(l) == 'string') {
42705                     add = {};
42706                     add[this.valueField] = l;
42707                     add[this.displayField] = l
42708                 }
42709                 this.addItem(add);
42710             }, this);
42711              
42712         }
42713         
42714         
42715     },
42716     setFromData: function(v)
42717     {
42718         // this recieves an object, if setValues is called.
42719         this.reset();
42720         this.el.dom.value = v[this.displayField];
42721         this.hiddenEl.dom.value = v[this.valueField];
42722         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42723             return;
42724         }
42725         var kv = v[this.valueField];
42726         var dv = v[this.displayField];
42727         kv = typeof(kv) != 'string' ? '' : kv;
42728         dv = typeof(dv) != 'string' ? '' : dv;
42729         
42730         
42731         var keys = kv.split(this.seperator);
42732         var display = dv.split(this.seperator);
42733         for (var i = 0 ; i < keys.length; i++) {
42734             add = {};
42735             add[this.valueField] = keys[i];
42736             add[this.displayField] = display[i];
42737             this.addItem(add);
42738         }
42739       
42740         
42741     },
42742     
42743     /**
42744      * Validates the combox array value
42745      * @return {Boolean} True if the value is valid, else false
42746      */
42747     validate : function(){
42748         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42749             this.clearInvalid();
42750             return true;
42751         }
42752         return false;
42753     },
42754     
42755     validateValue : function(value){
42756         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42757         
42758     },
42759     
42760     /*@
42761      * overide
42762      * 
42763      */
42764     isDirty : function() {
42765         if(this.disabled) {
42766             return false;
42767         }
42768         
42769         try {
42770             var d = Roo.decode(String(this.originalValue));
42771         } catch (e) {
42772             return String(this.getValue()) !== String(this.originalValue);
42773         }
42774         
42775         var originalValue = [];
42776         
42777         for (var i = 0; i < d.length; i++){
42778             originalValue.push(d[i][this.valueField]);
42779         }
42780         
42781         return String(this.getValue()) !== String(originalValue.join(this.seperator));
42782         
42783     }
42784     
42785 });
42786
42787
42788
42789 /**
42790  * @class Roo.form.ComboBoxArray.Item
42791  * @extends Roo.BoxComponent
42792  * A selected item in the list
42793  *  Fred [x]  Brian [x]  [Pick another |v]
42794  * 
42795  * @constructor
42796  * Create a new item.
42797  * @param {Object} config Configuration options
42798  */
42799  
42800 Roo.form.ComboBoxArray.Item = function(config) {
42801     config.id = Roo.id();
42802     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42803 }
42804
42805 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42806     data : {},
42807     cb: false,
42808     displayField : false,
42809     tipField : false,
42810     
42811     
42812     defaultAutoCreate : {
42813         tag: 'div',
42814         cls: 'x-cbarray-item',
42815         cn : [ 
42816             { tag: 'div' },
42817             {
42818                 tag: 'img',
42819                 width:16,
42820                 height : 16,
42821                 src : Roo.BLANK_IMAGE_URL ,
42822                 align: 'center'
42823             }
42824         ]
42825         
42826     },
42827     
42828  
42829     onRender : function(ct, position)
42830     {
42831         Roo.form.Field.superclass.onRender.call(this, ct, position);
42832         
42833         if(!this.el){
42834             var cfg = this.getAutoCreate();
42835             this.el = ct.createChild(cfg, position);
42836         }
42837         
42838         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42839         
42840         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42841             this.cb.renderer(this.data) :
42842             String.format('{0}',this.data[this.displayField]);
42843         
42844             
42845         this.el.child('div').dom.setAttribute('qtip',
42846                         String.format('{0}',this.data[this.tipField])
42847         );
42848         
42849         this.el.child('img').on('click', this.remove, this);
42850         
42851     },
42852    
42853     remove : function()
42854     {
42855         if(this.cb.disabled){
42856             return;
42857         }
42858         
42859         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42860             this.cb.items.remove(this);
42861             this.el.child('img').un('click', this.remove, this);
42862             this.el.remove();
42863             this.cb.updateHiddenEl();
42864
42865             this.cb.fireEvent('remove', this.cb, this);
42866         }
42867         
42868     }
42869 });/*
42870  * RooJS Library 1.1.1
42871  * Copyright(c) 2008-2011  Alan Knowles
42872  *
42873  * License - LGPL
42874  */
42875  
42876
42877 /**
42878  * @class Roo.form.ComboNested
42879  * @extends Roo.form.ComboBox
42880  * A combobox for that allows selection of nested items in a list,
42881  * eg.
42882  *
42883  *  Book
42884  *    -> red
42885  *    -> green
42886  *  Table
42887  *    -> square
42888  *      ->red
42889  *      ->green
42890  *    -> rectangle
42891  *      ->green
42892  *      
42893  * 
42894  * @constructor
42895  * Create a new ComboNested
42896  * @param {Object} config Configuration options
42897  */
42898 Roo.form.ComboNested = function(config){
42899     Roo.form.ComboCheck.superclass.constructor.call(this, config);
42900     // should verify some data...
42901     // like
42902     // hiddenName = required..
42903     // displayField = required
42904     // valudField == required
42905     var req= [ 'hiddenName', 'displayField', 'valueField' ];
42906     var _t = this;
42907     Roo.each(req, function(e) {
42908         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
42909             throw "Roo.form.ComboNested : missing value for: " + e;
42910         }
42911     });
42912      
42913     
42914 };
42915
42916 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
42917    
42918     /*
42919      * @config {Number} max Number of columns to show
42920      */
42921     
42922     maxColumns : 3,
42923    
42924     list : null, // the outermost div..
42925     innerLists : null, // the
42926     views : null,
42927     stores : null,
42928     // private
42929     loadingChildren : false,
42930     
42931     onRender : function(ct, position)
42932     {
42933         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
42934         
42935         if(this.hiddenName){
42936             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42937                     'before', true);
42938             this.hiddenField.value =
42939                 this.hiddenValue !== undefined ? this.hiddenValue :
42940                 this.value !== undefined ? this.value : '';
42941
42942             // prevent input submission
42943             this.el.dom.removeAttribute('name');
42944              
42945              
42946         }
42947         
42948         if(Roo.isGecko){
42949             this.el.dom.setAttribute('autocomplete', 'off');
42950         }
42951
42952         var cls = 'x-combo-list';
42953
42954         this.list = new Roo.Layer({
42955             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42956         });
42957
42958         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42959         this.list.setWidth(lw);
42960         this.list.swallowEvent('mousewheel');
42961         this.assetHeight = 0;
42962
42963         if(this.title){
42964             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42965             this.assetHeight += this.header.getHeight();
42966         }
42967         this.innerLists = [];
42968         this.views = [];
42969         this.stores = [];
42970         for (var i =0 ; i < this.maxColumns; i++) {
42971             this.onRenderList( cls, i);
42972         }
42973         
42974         // always needs footer, as we are going to have an 'OK' button.
42975         this.footer = this.list.createChild({cls:cls+'-ft'});
42976         this.pageTb = new Roo.Toolbar(this.footer);  
42977         var _this = this;
42978         this.pageTb.add(  {
42979             
42980             text: 'Done',
42981             handler: function()
42982             {
42983                 _this.collapse();
42984             }
42985         });
42986         
42987         if ( this.allowBlank && !this.disableClear) {
42988             
42989             this.pageTb.add(new Roo.Toolbar.Fill(), {
42990                 cls: 'x-btn-icon x-btn-clear',
42991                 text: '&#160;',
42992                 handler: function()
42993                 {
42994                     _this.collapse();
42995                     _this.clearValue();
42996                     _this.onSelect(false, -1);
42997                 }
42998             });
42999         }
43000         if (this.footer) {
43001             this.assetHeight += this.footer.getHeight();
43002         }
43003         
43004     },
43005     onRenderList : function (  cls, i)
43006     {
43007         
43008         var lw = Math.floor(
43009                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43010         );
43011         
43012         this.list.setWidth(lw); // default to '1'
43013
43014         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43015         //il.on('mouseover', this.onViewOver, this, { list:  i });
43016         //il.on('mousemove', this.onViewMove, this, { list:  i });
43017         il.setWidth(lw);
43018         il.setStyle({ 'overflow-x' : 'hidden'});
43019
43020         if(!this.tpl){
43021             this.tpl = new Roo.Template({
43022                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43023                 isEmpty: function (value, allValues) {
43024                     //Roo.log(value);
43025                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43026                     return dl ? 'has-children' : 'no-children'
43027                 }
43028             });
43029         }
43030         
43031         var store  = this.store;
43032         if (i > 0) {
43033             store  = new Roo.data.SimpleStore({
43034                 //fields : this.store.reader.meta.fields,
43035                 reader : this.store.reader,
43036                 data : [ ]
43037             });
43038         }
43039         this.stores[i]  = store;
43040                   
43041         var view = this.views[i] = new Roo.View(
43042             il,
43043             this.tpl,
43044             {
43045                 singleSelect:true,
43046                 store: store,
43047                 selectedClass: this.selectedClass
43048             }
43049         );
43050         view.getEl().setWidth(lw);
43051         view.getEl().setStyle({
43052             position: i < 1 ? 'relative' : 'absolute',
43053             top: 0,
43054             left: (i * lw ) + 'px',
43055             display : i > 0 ? 'none' : 'block'
43056         });
43057         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43058         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43059         //view.on('click', this.onViewClick, this, { list : i });
43060
43061         store.on('beforeload', this.onBeforeLoad, this);
43062         store.on('load',  this.onLoad, this, { list  : i});
43063         store.on('loadexception', this.onLoadException, this);
43064
43065         // hide the other vies..
43066         
43067         
43068         
43069     },
43070       
43071     restrictHeight : function()
43072     {
43073         var mh = 0;
43074         Roo.each(this.innerLists, function(il,i) {
43075             var el = this.views[i].getEl();
43076             el.dom.style.height = '';
43077             var inner = el.dom;
43078             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
43079             // only adjust heights on other ones..
43080             mh = Math.max(h, mh);
43081             if (i < 1) {
43082                 
43083                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43084                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43085                
43086             }
43087             
43088             
43089         }, this);
43090         
43091         this.list.beginUpdate();
43092         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
43093         this.list.alignTo(this.el, this.listAlign);
43094         this.list.endUpdate();
43095         
43096     },
43097      
43098     
43099     // -- store handlers..
43100     // private
43101     onBeforeLoad : function()
43102     {
43103         if(!this.hasFocus){
43104             return;
43105         }
43106         this.innerLists[0].update(this.loadingText ?
43107                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43108         this.restrictHeight();
43109         this.selectedIndex = -1;
43110     },
43111     // private
43112     onLoad : function(a,b,c,d)
43113     {
43114         if (!this.loadingChildren) {
43115             // then we are loading the top level. - hide the children
43116             for (var i = 1;i < this.views.length; i++) {
43117                 this.views[i].getEl().setStyle({ display : 'none' });
43118             }
43119             var lw = Math.floor(
43120                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43121             );
43122         
43123              this.list.setWidth(lw); // default to '1'
43124
43125             
43126         }
43127         if(!this.hasFocus){
43128             return;
43129         }
43130         
43131         if(this.store.getCount() > 0) {
43132             this.expand();
43133             this.restrictHeight();   
43134         } else {
43135             this.onEmptyResults();
43136         }
43137         
43138         if (!this.loadingChildren) {
43139             this.selectActive();
43140         }
43141         /*
43142         this.stores[1].loadData([]);
43143         this.stores[2].loadData([]);
43144         this.views
43145         */    
43146     
43147         //this.el.focus();
43148     },
43149     
43150     
43151     // private
43152     onLoadException : function()
43153     {
43154         this.collapse();
43155         Roo.log(this.store.reader.jsonData);
43156         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43157             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43158         }
43159         
43160         
43161     },
43162     // no cleaning of leading spaces on blur here.
43163     cleanLeadingSpace : function(e) { },
43164     
43165
43166     onSelectChange : function (view, sels, opts )
43167     {
43168         var ix = view.getSelectedIndexes();
43169          
43170         if (opts.list > this.maxColumns - 2) {
43171             if (view.store.getCount()<  1) {
43172                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
43173
43174             } else  {
43175                 if (ix.length) {
43176                     // used to clear ?? but if we are loading unselected 
43177                     this.setFromData(view.store.getAt(ix[0]).data);
43178                 }
43179                 
43180             }
43181             
43182             return;
43183         }
43184         
43185         if (!ix.length) {
43186             // this get's fired when trigger opens..
43187            // this.setFromData({});
43188             var str = this.stores[opts.list+1];
43189             str.data.clear(); // removeall wihtout the fire events..
43190             return;
43191         }
43192         
43193         var rec = view.store.getAt(ix[0]);
43194          
43195         this.setFromData(rec.data);
43196         this.fireEvent('select', this, rec, ix[0]);
43197         
43198         var lw = Math.floor(
43199              (
43200                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
43201              ) / this.maxColumns
43202         );
43203         this.loadingChildren = true;
43204         this.stores[opts.list+1].loadDataFromChildren( rec );
43205         this.loadingChildren = false;
43206         var dl = this.stores[opts.list+1]. getTotalCount();
43207         
43208         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
43209         
43210         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
43211         for (var i = opts.list+2; i < this.views.length;i++) {
43212             this.views[i].getEl().setStyle({ display : 'none' });
43213         }
43214         
43215         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
43216         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
43217         
43218         if (this.isLoading) {
43219            // this.selectActive(opts.list);
43220         }
43221          
43222     },
43223     
43224     
43225     
43226     
43227     onDoubleClick : function()
43228     {
43229         this.collapse(); //??
43230     },
43231     
43232      
43233     
43234     
43235     
43236     // private
43237     recordToStack : function(store, prop, value, stack)
43238     {
43239         var cstore = new Roo.data.SimpleStore({
43240             //fields : this.store.reader.meta.fields, // we need array reader.. for
43241             reader : this.store.reader,
43242             data : [ ]
43243         });
43244         var _this = this;
43245         var record  = false;
43246         var srec = false;
43247         if(store.getCount() < 1){
43248             return false;
43249         }
43250         store.each(function(r){
43251             if(r.data[prop] == value){
43252                 record = r;
43253             srec = r;
43254                 return false;
43255             }
43256             if (r.data.cn && r.data.cn.length) {
43257                 cstore.loadDataFromChildren( r);
43258                 var cret = _this.recordToStack(cstore, prop, value, stack);
43259                 if (cret !== false) {
43260                     record = cret;
43261                     srec = r;
43262                     return false;
43263                 }
43264             }
43265              
43266             return true;
43267         });
43268         if (record == false) {
43269             return false
43270         }
43271         stack.unshift(srec);
43272         return record;
43273     },
43274     
43275     /*
43276      * find the stack of stores that match our value.
43277      *
43278      * 
43279      */
43280     
43281     selectActive : function ()
43282     {
43283         // if store is not loaded, then we will need to wait for that to happen first.
43284         var stack = [];
43285         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
43286         for (var i = 0; i < stack.length; i++ ) {
43287             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
43288         }
43289         
43290     }
43291         
43292          
43293     
43294     
43295     
43296     
43297 });/*
43298  * Based on:
43299  * Ext JS Library 1.1.1
43300  * Copyright(c) 2006-2007, Ext JS, LLC.
43301  *
43302  * Originally Released Under LGPL - original licence link has changed is not relivant.
43303  *
43304  * Fork - LGPL
43305  * <script type="text/javascript">
43306  */
43307 /**
43308  * @class Roo.form.Checkbox
43309  * @extends Roo.form.Field
43310  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43311  * @constructor
43312  * Creates a new Checkbox
43313  * @param {Object} config Configuration options
43314  */
43315 Roo.form.Checkbox = function(config){
43316     Roo.form.Checkbox.superclass.constructor.call(this, config);
43317     this.addEvents({
43318         /**
43319          * @event check
43320          * Fires when the checkbox is checked or unchecked.
43321              * @param {Roo.form.Checkbox} this This checkbox
43322              * @param {Boolean} checked The new checked value
43323              */
43324         check : true
43325     });
43326 };
43327
43328 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43329     /**
43330      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43331      */
43332     focusClass : undefined,
43333     /**
43334      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43335      */
43336     fieldClass: "x-form-field",
43337     /**
43338      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43339      */
43340     checked: false,
43341     /**
43342      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43343      * {tag: "input", type: "checkbox", autocomplete: "off"})
43344      */
43345     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43346     /**
43347      * @cfg {String} boxLabel The text that appears beside the checkbox
43348      */
43349     boxLabel : "",
43350     /**
43351      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43352      */  
43353     inputValue : '1',
43354     /**
43355      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43356      */
43357      valueOff: '0', // value when not checked..
43358
43359     actionMode : 'viewEl', 
43360     //
43361     // private
43362     itemCls : 'x-menu-check-item x-form-item',
43363     groupClass : 'x-menu-group-item',
43364     inputType : 'hidden',
43365     
43366     
43367     inSetChecked: false, // check that we are not calling self...
43368     
43369     inputElement: false, // real input element?
43370     basedOn: false, // ????
43371     
43372     isFormField: true, // not sure where this is needed!!!!
43373
43374     onResize : function(){
43375         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43376         if(!this.boxLabel){
43377             this.el.alignTo(this.wrap, 'c-c');
43378         }
43379     },
43380
43381     initEvents : function(){
43382         Roo.form.Checkbox.superclass.initEvents.call(this);
43383         this.el.on("click", this.onClick,  this);
43384         this.el.on("change", this.onClick,  this);
43385     },
43386
43387
43388     getResizeEl : function(){
43389         return this.wrap;
43390     },
43391
43392     getPositionEl : function(){
43393         return this.wrap;
43394     },
43395
43396     // private
43397     onRender : function(ct, position){
43398         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43399         /*
43400         if(this.inputValue !== undefined){
43401             this.el.dom.value = this.inputValue;
43402         }
43403         */
43404         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43405         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43406         var viewEl = this.wrap.createChild({ 
43407             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43408         this.viewEl = viewEl;   
43409         this.wrap.on('click', this.onClick,  this); 
43410         
43411         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43412         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43413         
43414         
43415         
43416         if(this.boxLabel){
43417             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43418         //    viewEl.on('click', this.onClick,  this); 
43419         }
43420         //if(this.checked){
43421             this.setChecked(this.checked);
43422         //}else{
43423             //this.checked = this.el.dom;
43424         //}
43425
43426     },
43427
43428     // private
43429     initValue : Roo.emptyFn,
43430
43431     /**
43432      * Returns the checked state of the checkbox.
43433      * @return {Boolean} True if checked, else false
43434      */
43435     getValue : function(){
43436         if(this.el){
43437             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43438         }
43439         return this.valueOff;
43440         
43441     },
43442
43443         // private
43444     onClick : function(){ 
43445         if (this.disabled) {
43446             return;
43447         }
43448         this.setChecked(!this.checked);
43449
43450         //if(this.el.dom.checked != this.checked){
43451         //    this.setValue(this.el.dom.checked);
43452        // }
43453     },
43454
43455     /**
43456      * Sets the checked state of the checkbox.
43457      * On is always based on a string comparison between inputValue and the param.
43458      * @param {Boolean/String} value - the value to set 
43459      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43460      */
43461     setValue : function(v,suppressEvent){
43462         
43463         
43464         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43465         //if(this.el && this.el.dom){
43466         //    this.el.dom.checked = this.checked;
43467         //    this.el.dom.defaultChecked = this.checked;
43468         //}
43469         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43470         //this.fireEvent("check", this, this.checked);
43471     },
43472     // private..
43473     setChecked : function(state,suppressEvent)
43474     {
43475         if (this.inSetChecked) {
43476             this.checked = state;
43477             return;
43478         }
43479         
43480     
43481         if(this.wrap){
43482             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43483         }
43484         this.checked = state;
43485         if(suppressEvent !== true){
43486             this.fireEvent('check', this, state);
43487         }
43488         this.inSetChecked = true;
43489         this.el.dom.value = state ? this.inputValue : this.valueOff;
43490         this.inSetChecked = false;
43491         
43492     },
43493     // handle setting of hidden value by some other method!!?!?
43494     setFromHidden: function()
43495     {
43496         if(!this.el){
43497             return;
43498         }
43499         //console.log("SET FROM HIDDEN");
43500         //alert('setFrom hidden');
43501         this.setValue(this.el.dom.value);
43502     },
43503     
43504     onDestroy : function()
43505     {
43506         if(this.viewEl){
43507             Roo.get(this.viewEl).remove();
43508         }
43509          
43510         Roo.form.Checkbox.superclass.onDestroy.call(this);
43511     },
43512     
43513     setBoxLabel : function(str)
43514     {
43515         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43516     }
43517
43518 });/*
43519  * Based on:
43520  * Ext JS Library 1.1.1
43521  * Copyright(c) 2006-2007, Ext JS, LLC.
43522  *
43523  * Originally Released Under LGPL - original licence link has changed is not relivant.
43524  *
43525  * Fork - LGPL
43526  * <script type="text/javascript">
43527  */
43528  
43529 /**
43530  * @class Roo.form.Radio
43531  * @extends Roo.form.Checkbox
43532  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43533  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43534  * @constructor
43535  * Creates a new Radio
43536  * @param {Object} config Configuration options
43537  */
43538 Roo.form.Radio = function(){
43539     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43540 };
43541 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43542     inputType: 'radio',
43543
43544     /**
43545      * If this radio is part of a group, it will return the selected value
43546      * @return {String}
43547      */
43548     getGroupValue : function(){
43549         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43550     },
43551     
43552     
43553     onRender : function(ct, position){
43554         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43555         
43556         if(this.inputValue !== undefined){
43557             this.el.dom.value = this.inputValue;
43558         }
43559          
43560         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43561         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43562         //var viewEl = this.wrap.createChild({ 
43563         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43564         //this.viewEl = viewEl;   
43565         //this.wrap.on('click', this.onClick,  this); 
43566         
43567         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43568         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43569         
43570         
43571         
43572         if(this.boxLabel){
43573             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43574         //    viewEl.on('click', this.onClick,  this); 
43575         }
43576          if(this.checked){
43577             this.el.dom.checked =   'checked' ;
43578         }
43579          
43580     } 
43581     
43582     
43583 });//<script type="text/javascript">
43584
43585 /*
43586  * Based  Ext JS Library 1.1.1
43587  * Copyright(c) 2006-2007, Ext JS, LLC.
43588  * LGPL
43589  *
43590  */
43591  
43592 /**
43593  * @class Roo.HtmlEditorCore
43594  * @extends Roo.Component
43595  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43596  *
43597  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43598  */
43599
43600 Roo.HtmlEditorCore = function(config){
43601     
43602     
43603     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43604     
43605     
43606     this.addEvents({
43607         /**
43608          * @event initialize
43609          * Fires when the editor is fully initialized (including the iframe)
43610          * @param {Roo.HtmlEditorCore} this
43611          */
43612         initialize: true,
43613         /**
43614          * @event activate
43615          * Fires when the editor is first receives the focus. Any insertion must wait
43616          * until after this event.
43617          * @param {Roo.HtmlEditorCore} this
43618          */
43619         activate: true,
43620          /**
43621          * @event beforesync
43622          * Fires before the textarea is updated with content from the editor iframe. Return false
43623          * to cancel the sync.
43624          * @param {Roo.HtmlEditorCore} this
43625          * @param {String} html
43626          */
43627         beforesync: true,
43628          /**
43629          * @event beforepush
43630          * Fires before the iframe editor is updated with content from the textarea. Return false
43631          * to cancel the push.
43632          * @param {Roo.HtmlEditorCore} this
43633          * @param {String} html
43634          */
43635         beforepush: true,
43636          /**
43637          * @event sync
43638          * Fires when the textarea is updated with content from the editor iframe.
43639          * @param {Roo.HtmlEditorCore} this
43640          * @param {String} html
43641          */
43642         sync: true,
43643          /**
43644          * @event push
43645          * Fires when the iframe editor is updated with content from the textarea.
43646          * @param {Roo.HtmlEditorCore} this
43647          * @param {String} html
43648          */
43649         push: true,
43650         
43651         /**
43652          * @event editorevent
43653          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43654          * @param {Roo.HtmlEditorCore} this
43655          */
43656         editorevent: true
43657         
43658     });
43659     
43660     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43661     
43662     // defaults : white / black...
43663     this.applyBlacklists();
43664     
43665     
43666     
43667 };
43668
43669
43670 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43671
43672
43673      /**
43674      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43675      */
43676     
43677     owner : false,
43678     
43679      /**
43680      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43681      *                        Roo.resizable.
43682      */
43683     resizable : false,
43684      /**
43685      * @cfg {Number} height (in pixels)
43686      */   
43687     height: 300,
43688    /**
43689      * @cfg {Number} width (in pixels)
43690      */   
43691     width: 500,
43692     
43693     /**
43694      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43695      * 
43696      */
43697     stylesheets: false,
43698     
43699     // id of frame..
43700     frameId: false,
43701     
43702     // private properties
43703     validationEvent : false,
43704     deferHeight: true,
43705     initialized : false,
43706     activated : false,
43707     sourceEditMode : false,
43708     onFocus : Roo.emptyFn,
43709     iframePad:3,
43710     hideMode:'offsets',
43711     
43712     clearUp: true,
43713     
43714     // blacklist + whitelisted elements..
43715     black: false,
43716     white: false,
43717      
43718     bodyCls : '',
43719
43720     /**
43721      * Protected method that will not generally be called directly. It
43722      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43723      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43724      */
43725     getDocMarkup : function(){
43726         // body styles..
43727         var st = '';
43728         
43729         // inherit styels from page...?? 
43730         if (this.stylesheets === false) {
43731             
43732             Roo.get(document.head).select('style').each(function(node) {
43733                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43734             });
43735             
43736             Roo.get(document.head).select('link').each(function(node) { 
43737                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43738             });
43739             
43740         } else if (!this.stylesheets.length) {
43741                 // simple..
43742                 st = '<style type="text/css">' +
43743                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43744                    '</style>';
43745         } else {
43746             for (var i in this.stylesheets) { 
43747                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
43748             }
43749             
43750         }
43751         
43752         st +=  '<style type="text/css">' +
43753             'IMG { cursor: pointer } ' +
43754         '</style>';
43755
43756         var cls = 'roo-htmleditor-body';
43757         
43758         if(this.bodyCls.length){
43759             cls += ' ' + this.bodyCls;
43760         }
43761         
43762         return '<html><head>' + st  +
43763             //<style type="text/css">' +
43764             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43765             //'</style>' +
43766             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
43767     },
43768
43769     // private
43770     onRender : function(ct, position)
43771     {
43772         var _t = this;
43773         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43774         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43775         
43776         
43777         this.el.dom.style.border = '0 none';
43778         this.el.dom.setAttribute('tabIndex', -1);
43779         this.el.addClass('x-hidden hide');
43780         
43781         
43782         
43783         if(Roo.isIE){ // fix IE 1px bogus margin
43784             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43785         }
43786        
43787         
43788         this.frameId = Roo.id();
43789         
43790          
43791         
43792         var iframe = this.owner.wrap.createChild({
43793             tag: 'iframe',
43794             cls: 'form-control', // bootstrap..
43795             id: this.frameId,
43796             name: this.frameId,
43797             frameBorder : 'no',
43798             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43799         }, this.el
43800         );
43801         
43802         
43803         this.iframe = iframe.dom;
43804
43805          this.assignDocWin();
43806         
43807         this.doc.designMode = 'on';
43808        
43809         this.doc.open();
43810         this.doc.write(this.getDocMarkup());
43811         this.doc.close();
43812
43813         
43814         var task = { // must defer to wait for browser to be ready
43815             run : function(){
43816                 //console.log("run task?" + this.doc.readyState);
43817                 this.assignDocWin();
43818                 if(this.doc.body || this.doc.readyState == 'complete'){
43819                     try {
43820                         this.doc.designMode="on";
43821                     } catch (e) {
43822                         return;
43823                     }
43824                     Roo.TaskMgr.stop(task);
43825                     this.initEditor.defer(10, this);
43826                 }
43827             },
43828             interval : 10,
43829             duration: 10000,
43830             scope: this
43831         };
43832         Roo.TaskMgr.start(task);
43833
43834     },
43835
43836     // private
43837     onResize : function(w, h)
43838     {
43839          Roo.log('resize: ' +w + ',' + h );
43840         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43841         if(!this.iframe){
43842             return;
43843         }
43844         if(typeof w == 'number'){
43845             
43846             this.iframe.style.width = w + 'px';
43847         }
43848         if(typeof h == 'number'){
43849             
43850             this.iframe.style.height = h + 'px';
43851             if(this.doc){
43852                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43853             }
43854         }
43855         
43856     },
43857
43858     /**
43859      * Toggles the editor between standard and source edit mode.
43860      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43861      */
43862     toggleSourceEdit : function(sourceEditMode){
43863         
43864         this.sourceEditMode = sourceEditMode === true;
43865         
43866         if(this.sourceEditMode){
43867  
43868             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43869             
43870         }else{
43871             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43872             //this.iframe.className = '';
43873             this.deferFocus();
43874         }
43875         //this.setSize(this.owner.wrap.getSize());
43876         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43877     },
43878
43879     
43880   
43881
43882     /**
43883      * Protected method that will not generally be called directly. If you need/want
43884      * custom HTML cleanup, this is the method you should override.
43885      * @param {String} html The HTML to be cleaned
43886      * return {String} The cleaned HTML
43887      */
43888     cleanHtml : function(html){
43889         html = String(html);
43890         if(html.length > 5){
43891             if(Roo.isSafari){ // strip safari nonsense
43892                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43893             }
43894         }
43895         if(html == '&nbsp;'){
43896             html = '';
43897         }
43898         return html;
43899     },
43900
43901     /**
43902      * HTML Editor -> Textarea
43903      * Protected method that will not generally be called directly. Syncs the contents
43904      * of the editor iframe with the textarea.
43905      */
43906     syncValue : function(){
43907         if(this.initialized){
43908             var bd = (this.doc.body || this.doc.documentElement);
43909             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43910             var html = bd.innerHTML;
43911             if(Roo.isSafari){
43912                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43913                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43914                 if(m && m[1]){
43915                     html = '<div style="'+m[0]+'">' + html + '</div>';
43916                 }
43917             }
43918             html = this.cleanHtml(html);
43919             // fix up the special chars.. normaly like back quotes in word...
43920             // however we do not want to do this with chinese..
43921             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43922                 
43923                 var cc = match.charCodeAt();
43924
43925                 // Get the character value, handling surrogate pairs
43926                 if (match.length == 2) {
43927                     // It's a surrogate pair, calculate the Unicode code point
43928                     var high = match.charCodeAt(0) - 0xD800;
43929                     var low  = match.charCodeAt(1) - 0xDC00;
43930                     cc = (high * 0x400) + low + 0x10000;
43931                 }  else if (
43932                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43933                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43934                     (cc >= 0xf900 && cc < 0xfb00 )
43935                 ) {
43936                         return match;
43937                 }  
43938          
43939                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
43940                 return "&#" + cc + ";";
43941                 
43942                 
43943             });
43944             
43945             
43946              
43947             if(this.owner.fireEvent('beforesync', this, html) !== false){
43948                 this.el.dom.value = html;
43949                 this.owner.fireEvent('sync', this, html);
43950             }
43951         }
43952     },
43953
43954     /**
43955      * Protected method that will not generally be called directly. Pushes the value of the textarea
43956      * into the iframe editor.
43957      */
43958     pushValue : function(){
43959         if(this.initialized){
43960             var v = this.el.dom.value.trim();
43961             
43962 //            if(v.length < 1){
43963 //                v = '&#160;';
43964 //            }
43965             
43966             if(this.owner.fireEvent('beforepush', this, v) !== false){
43967                 var d = (this.doc.body || this.doc.documentElement);
43968                 d.innerHTML = v;
43969                 this.cleanUpPaste();
43970                 this.el.dom.value = d.innerHTML;
43971                 this.owner.fireEvent('push', this, v);
43972             }
43973         }
43974     },
43975
43976     // private
43977     deferFocus : function(){
43978         this.focus.defer(10, this);
43979     },
43980
43981     // doc'ed in Field
43982     focus : function(){
43983         if(this.win && !this.sourceEditMode){
43984             this.win.focus();
43985         }else{
43986             this.el.focus();
43987         }
43988     },
43989     
43990     assignDocWin: function()
43991     {
43992         var iframe = this.iframe;
43993         
43994          if(Roo.isIE){
43995             this.doc = iframe.contentWindow.document;
43996             this.win = iframe.contentWindow;
43997         } else {
43998 //            if (!Roo.get(this.frameId)) {
43999 //                return;
44000 //            }
44001 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44002 //            this.win = Roo.get(this.frameId).dom.contentWindow;
44003             
44004             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
44005                 return;
44006             }
44007             
44008             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44009             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
44010         }
44011     },
44012     
44013     // private
44014     initEditor : function(){
44015         //console.log("INIT EDITOR");
44016         this.assignDocWin();
44017         
44018         
44019         
44020         this.doc.designMode="on";
44021         this.doc.open();
44022         this.doc.write(this.getDocMarkup());
44023         this.doc.close();
44024         
44025         var dbody = (this.doc.body || this.doc.documentElement);
44026         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
44027         // this copies styles from the containing element into thsi one..
44028         // not sure why we need all of this..
44029         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
44030         
44031         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
44032         //ss['background-attachment'] = 'fixed'; // w3c
44033         dbody.bgProperties = 'fixed'; // ie
44034         //Roo.DomHelper.applyStyles(dbody, ss);
44035         Roo.EventManager.on(this.doc, {
44036             //'mousedown': this.onEditorEvent,
44037             'mouseup': this.onEditorEvent,
44038             'dblclick': this.onEditorEvent,
44039             'click': this.onEditorEvent,
44040             'keyup': this.onEditorEvent,
44041             buffer:100,
44042             scope: this
44043         });
44044         if(Roo.isGecko){
44045             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
44046         }
44047         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
44048             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
44049         }
44050         this.initialized = true;
44051
44052         this.owner.fireEvent('initialize', this);
44053         this.pushValue();
44054     },
44055
44056     // private
44057     onDestroy : function(){
44058         
44059         
44060         
44061         if(this.rendered){
44062             
44063             //for (var i =0; i < this.toolbars.length;i++) {
44064             //    // fixme - ask toolbars for heights?
44065             //    this.toolbars[i].onDestroy();
44066            // }
44067             
44068             //this.wrap.dom.innerHTML = '';
44069             //this.wrap.remove();
44070         }
44071     },
44072
44073     // private
44074     onFirstFocus : function(){
44075         
44076         this.assignDocWin();
44077         
44078         
44079         this.activated = true;
44080          
44081     
44082         if(Roo.isGecko){ // prevent silly gecko errors
44083             this.win.focus();
44084             var s = this.win.getSelection();
44085             if(!s.focusNode || s.focusNode.nodeType != 3){
44086                 var r = s.getRangeAt(0);
44087                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
44088                 r.collapse(true);
44089                 this.deferFocus();
44090             }
44091             try{
44092                 this.execCmd('useCSS', true);
44093                 this.execCmd('styleWithCSS', false);
44094             }catch(e){}
44095         }
44096         this.owner.fireEvent('activate', this);
44097     },
44098
44099     // private
44100     adjustFont: function(btn){
44101         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
44102         //if(Roo.isSafari){ // safari
44103         //    adjust *= 2;
44104        // }
44105         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
44106         if(Roo.isSafari){ // safari
44107             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
44108             v =  (v < 10) ? 10 : v;
44109             v =  (v > 48) ? 48 : v;
44110             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
44111             
44112         }
44113         
44114         
44115         v = Math.max(1, v+adjust);
44116         
44117         this.execCmd('FontSize', v  );
44118     },
44119
44120     onEditorEvent : function(e)
44121     {
44122         this.owner.fireEvent('editorevent', this, e);
44123       //  this.updateToolbar();
44124         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
44125     },
44126
44127     insertTag : function(tg)
44128     {
44129         // could be a bit smarter... -> wrap the current selected tRoo..
44130         if (tg.toLowerCase() == 'span' ||
44131             tg.toLowerCase() == 'code' ||
44132             tg.toLowerCase() == 'sup' ||
44133             tg.toLowerCase() == 'sub' 
44134             ) {
44135             
44136             range = this.createRange(this.getSelection());
44137             var wrappingNode = this.doc.createElement(tg.toLowerCase());
44138             wrappingNode.appendChild(range.extractContents());
44139             range.insertNode(wrappingNode);
44140
44141             return;
44142             
44143             
44144             
44145         }
44146         this.execCmd("formatblock",   tg);
44147         
44148     },
44149     
44150     insertText : function(txt)
44151     {
44152         
44153         
44154         var range = this.createRange();
44155         range.deleteContents();
44156                //alert(Sender.getAttribute('label'));
44157                
44158         range.insertNode(this.doc.createTextNode(txt));
44159     } ,
44160     
44161      
44162
44163     /**
44164      * Executes a Midas editor command on the editor document and performs necessary focus and
44165      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
44166      * @param {String} cmd The Midas command
44167      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44168      */
44169     relayCmd : function(cmd, value){
44170         this.win.focus();
44171         this.execCmd(cmd, value);
44172         this.owner.fireEvent('editorevent', this);
44173         //this.updateToolbar();
44174         this.owner.deferFocus();
44175     },
44176
44177     /**
44178      * Executes a Midas editor command directly on the editor document.
44179      * For visual commands, you should use {@link #relayCmd} instead.
44180      * <b>This should only be called after the editor is initialized.</b>
44181      * @param {String} cmd The Midas command
44182      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44183      */
44184     execCmd : function(cmd, value){
44185         this.doc.execCommand(cmd, false, value === undefined ? null : value);
44186         this.syncValue();
44187     },
44188  
44189  
44190    
44191     /**
44192      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
44193      * to insert tRoo.
44194      * @param {String} text | dom node.. 
44195      */
44196     insertAtCursor : function(text)
44197     {
44198         
44199         if(!this.activated){
44200             return;
44201         }
44202         /*
44203         if(Roo.isIE){
44204             this.win.focus();
44205             var r = this.doc.selection.createRange();
44206             if(r){
44207                 r.collapse(true);
44208                 r.pasteHTML(text);
44209                 this.syncValue();
44210                 this.deferFocus();
44211             
44212             }
44213             return;
44214         }
44215         */
44216         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
44217             this.win.focus();
44218             
44219             
44220             // from jquery ui (MIT licenced)
44221             var range, node;
44222             var win = this.win;
44223             
44224             if (win.getSelection && win.getSelection().getRangeAt) {
44225                 range = win.getSelection().getRangeAt(0);
44226                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
44227                 range.insertNode(node);
44228             } else if (win.document.selection && win.document.selection.createRange) {
44229                 // no firefox support
44230                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44231                 win.document.selection.createRange().pasteHTML(txt);
44232             } else {
44233                 // no firefox support
44234                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44235                 this.execCmd('InsertHTML', txt);
44236             } 
44237             
44238             this.syncValue();
44239             
44240             this.deferFocus();
44241         }
44242     },
44243  // private
44244     mozKeyPress : function(e){
44245         if(e.ctrlKey){
44246             var c = e.getCharCode(), cmd;
44247           
44248             if(c > 0){
44249                 c = String.fromCharCode(c).toLowerCase();
44250                 switch(c){
44251                     case 'b':
44252                         cmd = 'bold';
44253                         break;
44254                     case 'i':
44255                         cmd = 'italic';
44256                         break;
44257                     
44258                     case 'u':
44259                         cmd = 'underline';
44260                         break;
44261                     
44262                     case 'v':
44263                         this.cleanUpPaste.defer(100, this);
44264                         return;
44265                         
44266                 }
44267                 if(cmd){
44268                     this.win.focus();
44269                     this.execCmd(cmd);
44270                     this.deferFocus();
44271                     e.preventDefault();
44272                 }
44273                 
44274             }
44275         }
44276     },
44277
44278     // private
44279     fixKeys : function(){ // load time branching for fastest keydown performance
44280         if(Roo.isIE){
44281             return function(e){
44282                 var k = e.getKey(), r;
44283                 if(k == e.TAB){
44284                     e.stopEvent();
44285                     r = this.doc.selection.createRange();
44286                     if(r){
44287                         r.collapse(true);
44288                         r.pasteHTML('&#160;&#160;&#160;&#160;');
44289                         this.deferFocus();
44290                     }
44291                     return;
44292                 }
44293                 
44294                 if(k == e.ENTER){
44295                     r = this.doc.selection.createRange();
44296                     if(r){
44297                         var target = r.parentElement();
44298                         if(!target || target.tagName.toLowerCase() != 'li'){
44299                             e.stopEvent();
44300                             r.pasteHTML('<br />');
44301                             r.collapse(false);
44302                             r.select();
44303                         }
44304                     }
44305                 }
44306                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44307                     this.cleanUpPaste.defer(100, this);
44308                     return;
44309                 }
44310                 
44311                 
44312             };
44313         }else if(Roo.isOpera){
44314             return function(e){
44315                 var k = e.getKey();
44316                 if(k == e.TAB){
44317                     e.stopEvent();
44318                     this.win.focus();
44319                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44320                     this.deferFocus();
44321                 }
44322                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44323                     this.cleanUpPaste.defer(100, this);
44324                     return;
44325                 }
44326                 
44327             };
44328         }else if(Roo.isSafari){
44329             return function(e){
44330                 var k = e.getKey();
44331                 
44332                 if(k == e.TAB){
44333                     e.stopEvent();
44334                     this.execCmd('InsertText','\t');
44335                     this.deferFocus();
44336                     return;
44337                 }
44338                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44339                     this.cleanUpPaste.defer(100, this);
44340                     return;
44341                 }
44342                 
44343              };
44344         }
44345     }(),
44346     
44347     getAllAncestors: function()
44348     {
44349         var p = this.getSelectedNode();
44350         var a = [];
44351         if (!p) {
44352             a.push(p); // push blank onto stack..
44353             p = this.getParentElement();
44354         }
44355         
44356         
44357         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44358             a.push(p);
44359             p = p.parentNode;
44360         }
44361         a.push(this.doc.body);
44362         return a;
44363     },
44364     lastSel : false,
44365     lastSelNode : false,
44366     
44367     
44368     getSelection : function() 
44369     {
44370         this.assignDocWin();
44371         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44372     },
44373     
44374     getSelectedNode: function() 
44375     {
44376         // this may only work on Gecko!!!
44377         
44378         // should we cache this!!!!
44379         
44380         
44381         
44382          
44383         var range = this.createRange(this.getSelection()).cloneRange();
44384         
44385         if (Roo.isIE) {
44386             var parent = range.parentElement();
44387             while (true) {
44388                 var testRange = range.duplicate();
44389                 testRange.moveToElementText(parent);
44390                 if (testRange.inRange(range)) {
44391                     break;
44392                 }
44393                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44394                     break;
44395                 }
44396                 parent = parent.parentElement;
44397             }
44398             return parent;
44399         }
44400         
44401         // is ancestor a text element.
44402         var ac =  range.commonAncestorContainer;
44403         if (ac.nodeType == 3) {
44404             ac = ac.parentNode;
44405         }
44406         
44407         var ar = ac.childNodes;
44408          
44409         var nodes = [];
44410         var other_nodes = [];
44411         var has_other_nodes = false;
44412         for (var i=0;i<ar.length;i++) {
44413             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44414                 continue;
44415             }
44416             // fullly contained node.
44417             
44418             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44419                 nodes.push(ar[i]);
44420                 continue;
44421             }
44422             
44423             // probably selected..
44424             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44425                 other_nodes.push(ar[i]);
44426                 continue;
44427             }
44428             // outer..
44429             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44430                 continue;
44431             }
44432             
44433             
44434             has_other_nodes = true;
44435         }
44436         if (!nodes.length && other_nodes.length) {
44437             nodes= other_nodes;
44438         }
44439         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44440             return false;
44441         }
44442         
44443         return nodes[0];
44444     },
44445     createRange: function(sel)
44446     {
44447         // this has strange effects when using with 
44448         // top toolbar - not sure if it's a great idea.
44449         //this.editor.contentWindow.focus();
44450         if (typeof sel != "undefined") {
44451             try {
44452                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44453             } catch(e) {
44454                 return this.doc.createRange();
44455             }
44456         } else {
44457             return this.doc.createRange();
44458         }
44459     },
44460     getParentElement: function()
44461     {
44462         
44463         this.assignDocWin();
44464         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44465         
44466         var range = this.createRange(sel);
44467          
44468         try {
44469             var p = range.commonAncestorContainer;
44470             while (p.nodeType == 3) { // text node
44471                 p = p.parentNode;
44472             }
44473             return p;
44474         } catch (e) {
44475             return null;
44476         }
44477     
44478     },
44479     /***
44480      *
44481      * Range intersection.. the hard stuff...
44482      *  '-1' = before
44483      *  '0' = hits..
44484      *  '1' = after.
44485      *         [ -- selected range --- ]
44486      *   [fail]                        [fail]
44487      *
44488      *    basically..
44489      *      if end is before start or  hits it. fail.
44490      *      if start is after end or hits it fail.
44491      *
44492      *   if either hits (but other is outside. - then it's not 
44493      *   
44494      *    
44495      **/
44496     
44497     
44498     // @see http://www.thismuchiknow.co.uk/?p=64.
44499     rangeIntersectsNode : function(range, node)
44500     {
44501         var nodeRange = node.ownerDocument.createRange();
44502         try {
44503             nodeRange.selectNode(node);
44504         } catch (e) {
44505             nodeRange.selectNodeContents(node);
44506         }
44507     
44508         var rangeStartRange = range.cloneRange();
44509         rangeStartRange.collapse(true);
44510     
44511         var rangeEndRange = range.cloneRange();
44512         rangeEndRange.collapse(false);
44513     
44514         var nodeStartRange = nodeRange.cloneRange();
44515         nodeStartRange.collapse(true);
44516     
44517         var nodeEndRange = nodeRange.cloneRange();
44518         nodeEndRange.collapse(false);
44519     
44520         return rangeStartRange.compareBoundaryPoints(
44521                  Range.START_TO_START, nodeEndRange) == -1 &&
44522                rangeEndRange.compareBoundaryPoints(
44523                  Range.START_TO_START, nodeStartRange) == 1;
44524         
44525          
44526     },
44527     rangeCompareNode : function(range, node)
44528     {
44529         var nodeRange = node.ownerDocument.createRange();
44530         try {
44531             nodeRange.selectNode(node);
44532         } catch (e) {
44533             nodeRange.selectNodeContents(node);
44534         }
44535         
44536         
44537         range.collapse(true);
44538     
44539         nodeRange.collapse(true);
44540      
44541         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44542         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44543          
44544         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44545         
44546         var nodeIsBefore   =  ss == 1;
44547         var nodeIsAfter    = ee == -1;
44548         
44549         if (nodeIsBefore && nodeIsAfter) {
44550             return 0; // outer
44551         }
44552         if (!nodeIsBefore && nodeIsAfter) {
44553             return 1; //right trailed.
44554         }
44555         
44556         if (nodeIsBefore && !nodeIsAfter) {
44557             return 2;  // left trailed.
44558         }
44559         // fully contined.
44560         return 3;
44561     },
44562
44563     // private? - in a new class?
44564     cleanUpPaste :  function()
44565     {
44566         // cleans up the whole document..
44567         Roo.log('cleanuppaste');
44568         
44569         this.cleanUpChildren(this.doc.body);
44570         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44571         if (clean != this.doc.body.innerHTML) {
44572             this.doc.body.innerHTML = clean;
44573         }
44574         
44575     },
44576     
44577     cleanWordChars : function(input) {// change the chars to hex code
44578         var he = Roo.HtmlEditorCore;
44579         
44580         var output = input;
44581         Roo.each(he.swapCodes, function(sw) { 
44582             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44583             
44584             output = output.replace(swapper, sw[1]);
44585         });
44586         
44587         return output;
44588     },
44589     
44590     
44591     cleanUpChildren : function (n)
44592     {
44593         if (!n.childNodes.length) {
44594             return;
44595         }
44596         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44597            this.cleanUpChild(n.childNodes[i]);
44598         }
44599     },
44600     
44601     
44602         
44603     
44604     cleanUpChild : function (node)
44605     {
44606         var ed = this;
44607         //console.log(node);
44608         if (node.nodeName == "#text") {
44609             // clean up silly Windows -- stuff?
44610             return; 
44611         }
44612         if (node.nodeName == "#comment") {
44613             node.parentNode.removeChild(node);
44614             // clean up silly Windows -- stuff?
44615             return; 
44616         }
44617         var lcname = node.tagName.toLowerCase();
44618         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44619         // whitelist of tags..
44620         
44621         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44622             // remove node.
44623             node.parentNode.removeChild(node);
44624             return;
44625             
44626         }
44627         
44628         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44629         
44630         // spans with no attributes - just remove them..
44631         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44632             remove_keep_children = true;
44633         }
44634         
44635         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44636         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44637         
44638         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44639         //    remove_keep_children = true;
44640         //}
44641         
44642         if (remove_keep_children) {
44643             this.cleanUpChildren(node);
44644             // inserts everything just before this node...
44645             while (node.childNodes.length) {
44646                 var cn = node.childNodes[0];
44647                 node.removeChild(cn);
44648                 node.parentNode.insertBefore(cn, node);
44649             }
44650             node.parentNode.removeChild(node);
44651             return;
44652         }
44653         
44654         if (!node.attributes || !node.attributes.length) {
44655             
44656           
44657             
44658             
44659             this.cleanUpChildren(node);
44660             return;
44661         }
44662         
44663         function cleanAttr(n,v)
44664         {
44665             
44666             if (v.match(/^\./) || v.match(/^\//)) {
44667                 return;
44668             }
44669             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44670                 return;
44671             }
44672             if (v.match(/^#/)) {
44673                 return;
44674             }
44675             if (v.match(/^\{/)) { // allow template editing.
44676                 return;
44677             }
44678 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44679             node.removeAttribute(n);
44680             
44681         }
44682         
44683         var cwhite = this.cwhite;
44684         var cblack = this.cblack;
44685             
44686         function cleanStyle(n,v)
44687         {
44688             if (v.match(/expression/)) { //XSS?? should we even bother..
44689                 node.removeAttribute(n);
44690                 return;
44691             }
44692             
44693             var parts = v.split(/;/);
44694             var clean = [];
44695             
44696             Roo.each(parts, function(p) {
44697                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44698                 if (!p.length) {
44699                     return true;
44700                 }
44701                 var l = p.split(':').shift().replace(/\s+/g,'');
44702                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44703                 
44704                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44705 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44706                     //node.removeAttribute(n);
44707                     return true;
44708                 }
44709                 //Roo.log()
44710                 // only allow 'c whitelisted system attributes'
44711                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44712 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44713                     //node.removeAttribute(n);
44714                     return true;
44715                 }
44716                 
44717                 
44718                  
44719                 
44720                 clean.push(p);
44721                 return true;
44722             });
44723             if (clean.length) { 
44724                 node.setAttribute(n, clean.join(';'));
44725             } else {
44726                 node.removeAttribute(n);
44727             }
44728             
44729         }
44730         
44731         
44732         for (var i = node.attributes.length-1; i > -1 ; i--) {
44733             var a = node.attributes[i];
44734             //console.log(a);
44735             
44736             if (a.name.toLowerCase().substr(0,2)=='on')  {
44737                 node.removeAttribute(a.name);
44738                 continue;
44739             }
44740             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44741                 node.removeAttribute(a.name);
44742                 continue;
44743             }
44744             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44745                 cleanAttr(a.name,a.value); // fixme..
44746                 continue;
44747             }
44748             if (a.name == 'style') {
44749                 cleanStyle(a.name,a.value);
44750                 continue;
44751             }
44752             /// clean up MS crap..
44753             // tecnically this should be a list of valid class'es..
44754             
44755             
44756             if (a.name == 'class') {
44757                 if (a.value.match(/^Mso/)) {
44758                     node.removeAttribute('class');
44759                 }
44760                 
44761                 if (a.value.match(/^body$/)) {
44762                     node.removeAttribute('class');
44763                 }
44764                 continue;
44765             }
44766             
44767             // style cleanup!?
44768             // class cleanup?
44769             
44770         }
44771         
44772         
44773         this.cleanUpChildren(node);
44774         
44775         
44776     },
44777     
44778     /**
44779      * Clean up MS wordisms...
44780      */
44781     cleanWord : function(node)
44782     {
44783         if (!node) {
44784             this.cleanWord(this.doc.body);
44785             return;
44786         }
44787         
44788         if(
44789                 node.nodeName == 'SPAN' &&
44790                 !node.hasAttributes() &&
44791                 node.childNodes.length == 1 &&
44792                 node.firstChild.nodeName == "#text"  
44793         ) {
44794             var textNode = node.firstChild;
44795             node.removeChild(textNode);
44796             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44797                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44798             }
44799             node.parentNode.insertBefore(textNode, node);
44800             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44801                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44802             }
44803             node.parentNode.removeChild(node);
44804         }
44805         
44806         if (node.nodeName == "#text") {
44807             // clean up silly Windows -- stuff?
44808             return; 
44809         }
44810         if (node.nodeName == "#comment") {
44811             node.parentNode.removeChild(node);
44812             // clean up silly Windows -- stuff?
44813             return; 
44814         }
44815         
44816         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44817             node.parentNode.removeChild(node);
44818             return;
44819         }
44820         //Roo.log(node.tagName);
44821         // remove - but keep children..
44822         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44823             //Roo.log('-- removed');
44824             while (node.childNodes.length) {
44825                 var cn = node.childNodes[0];
44826                 node.removeChild(cn);
44827                 node.parentNode.insertBefore(cn, node);
44828                 // move node to parent - and clean it..
44829                 this.cleanWord(cn);
44830             }
44831             node.parentNode.removeChild(node);
44832             /// no need to iterate chidlren = it's got none..
44833             //this.iterateChildren(node, this.cleanWord);
44834             return;
44835         }
44836         // clean styles
44837         if (node.className.length) {
44838             
44839             var cn = node.className.split(/\W+/);
44840             var cna = [];
44841             Roo.each(cn, function(cls) {
44842                 if (cls.match(/Mso[a-zA-Z]+/)) {
44843                     return;
44844                 }
44845                 cna.push(cls);
44846             });
44847             node.className = cna.length ? cna.join(' ') : '';
44848             if (!cna.length) {
44849                 node.removeAttribute("class");
44850             }
44851         }
44852         
44853         if (node.hasAttribute("lang")) {
44854             node.removeAttribute("lang");
44855         }
44856         
44857         if (node.hasAttribute("style")) {
44858             
44859             var styles = node.getAttribute("style").split(";");
44860             var nstyle = [];
44861             Roo.each(styles, function(s) {
44862                 if (!s.match(/:/)) {
44863                     return;
44864                 }
44865                 var kv = s.split(":");
44866                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44867                     return;
44868                 }
44869                 // what ever is left... we allow.
44870                 nstyle.push(s);
44871             });
44872             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44873             if (!nstyle.length) {
44874                 node.removeAttribute('style');
44875             }
44876         }
44877         this.iterateChildren(node, this.cleanWord);
44878         
44879         
44880         
44881     },
44882     /**
44883      * iterateChildren of a Node, calling fn each time, using this as the scole..
44884      * @param {DomNode} node node to iterate children of.
44885      * @param {Function} fn method of this class to call on each item.
44886      */
44887     iterateChildren : function(node, fn)
44888     {
44889         if (!node.childNodes.length) {
44890                 return;
44891         }
44892         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44893            fn.call(this, node.childNodes[i])
44894         }
44895     },
44896     
44897     
44898     /**
44899      * cleanTableWidths.
44900      *
44901      * Quite often pasting from word etc.. results in tables with column and widths.
44902      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44903      *
44904      */
44905     cleanTableWidths : function(node)
44906     {
44907          
44908          
44909         if (!node) {
44910             this.cleanTableWidths(this.doc.body);
44911             return;
44912         }
44913         
44914         // ignore list...
44915         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44916             return; 
44917         }
44918         Roo.log(node.tagName);
44919         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44920             this.iterateChildren(node, this.cleanTableWidths);
44921             return;
44922         }
44923         if (node.hasAttribute('width')) {
44924             node.removeAttribute('width');
44925         }
44926         
44927          
44928         if (node.hasAttribute("style")) {
44929             // pretty basic...
44930             
44931             var styles = node.getAttribute("style").split(";");
44932             var nstyle = [];
44933             Roo.each(styles, function(s) {
44934                 if (!s.match(/:/)) {
44935                     return;
44936                 }
44937                 var kv = s.split(":");
44938                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44939                     return;
44940                 }
44941                 // what ever is left... we allow.
44942                 nstyle.push(s);
44943             });
44944             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44945             if (!nstyle.length) {
44946                 node.removeAttribute('style');
44947             }
44948         }
44949         
44950         this.iterateChildren(node, this.cleanTableWidths);
44951         
44952         
44953     },
44954     
44955     
44956     
44957     
44958     domToHTML : function(currentElement, depth, nopadtext) {
44959         
44960         depth = depth || 0;
44961         nopadtext = nopadtext || false;
44962     
44963         if (!currentElement) {
44964             return this.domToHTML(this.doc.body);
44965         }
44966         
44967         //Roo.log(currentElement);
44968         var j;
44969         var allText = false;
44970         var nodeName = currentElement.nodeName;
44971         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44972         
44973         if  (nodeName == '#text') {
44974             
44975             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44976         }
44977         
44978         
44979         var ret = '';
44980         if (nodeName != 'BODY') {
44981              
44982             var i = 0;
44983             // Prints the node tagName, such as <A>, <IMG>, etc
44984             if (tagName) {
44985                 var attr = [];
44986                 for(i = 0; i < currentElement.attributes.length;i++) {
44987                     // quoting?
44988                     var aname = currentElement.attributes.item(i).name;
44989                     if (!currentElement.attributes.item(i).value.length) {
44990                         continue;
44991                     }
44992                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44993                 }
44994                 
44995                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44996             } 
44997             else {
44998                 
44999                 // eack
45000             }
45001         } else {
45002             tagName = false;
45003         }
45004         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
45005             return ret;
45006         }
45007         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
45008             nopadtext = true;
45009         }
45010         
45011         
45012         // Traverse the tree
45013         i = 0;
45014         var currentElementChild = currentElement.childNodes.item(i);
45015         var allText = true;
45016         var innerHTML  = '';
45017         lastnode = '';
45018         while (currentElementChild) {
45019             // Formatting code (indent the tree so it looks nice on the screen)
45020             var nopad = nopadtext;
45021             if (lastnode == 'SPAN') {
45022                 nopad  = true;
45023             }
45024             // text
45025             if  (currentElementChild.nodeName == '#text') {
45026                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
45027                 toadd = nopadtext ? toadd : toadd.trim();
45028                 if (!nopad && toadd.length > 80) {
45029                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
45030                 }
45031                 innerHTML  += toadd;
45032                 
45033                 i++;
45034                 currentElementChild = currentElement.childNodes.item(i);
45035                 lastNode = '';
45036                 continue;
45037             }
45038             allText = false;
45039             
45040             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
45041                 
45042             // Recursively traverse the tree structure of the child node
45043             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
45044             lastnode = currentElementChild.nodeName;
45045             i++;
45046             currentElementChild=currentElement.childNodes.item(i);
45047         }
45048         
45049         ret += innerHTML;
45050         
45051         if (!allText) {
45052                 // The remaining code is mostly for formatting the tree
45053             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
45054         }
45055         
45056         
45057         if (tagName) {
45058             ret+= "</"+tagName+">";
45059         }
45060         return ret;
45061         
45062     },
45063         
45064     applyBlacklists : function()
45065     {
45066         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
45067         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
45068         
45069         this.white = [];
45070         this.black = [];
45071         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
45072             if (b.indexOf(tag) > -1) {
45073                 return;
45074             }
45075             this.white.push(tag);
45076             
45077         }, this);
45078         
45079         Roo.each(w, function(tag) {
45080             if (b.indexOf(tag) > -1) {
45081                 return;
45082             }
45083             if (this.white.indexOf(tag) > -1) {
45084                 return;
45085             }
45086             this.white.push(tag);
45087             
45088         }, this);
45089         
45090         
45091         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
45092             if (w.indexOf(tag) > -1) {
45093                 return;
45094             }
45095             this.black.push(tag);
45096             
45097         }, this);
45098         
45099         Roo.each(b, function(tag) {
45100             if (w.indexOf(tag) > -1) {
45101                 return;
45102             }
45103             if (this.black.indexOf(tag) > -1) {
45104                 return;
45105             }
45106             this.black.push(tag);
45107             
45108         }, this);
45109         
45110         
45111         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
45112         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
45113         
45114         this.cwhite = [];
45115         this.cblack = [];
45116         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
45117             if (b.indexOf(tag) > -1) {
45118                 return;
45119             }
45120             this.cwhite.push(tag);
45121             
45122         }, this);
45123         
45124         Roo.each(w, function(tag) {
45125             if (b.indexOf(tag) > -1) {
45126                 return;
45127             }
45128             if (this.cwhite.indexOf(tag) > -1) {
45129                 return;
45130             }
45131             this.cwhite.push(tag);
45132             
45133         }, this);
45134         
45135         
45136         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
45137             if (w.indexOf(tag) > -1) {
45138                 return;
45139             }
45140             this.cblack.push(tag);
45141             
45142         }, this);
45143         
45144         Roo.each(b, function(tag) {
45145             if (w.indexOf(tag) > -1) {
45146                 return;
45147             }
45148             if (this.cblack.indexOf(tag) > -1) {
45149                 return;
45150             }
45151             this.cblack.push(tag);
45152             
45153         }, this);
45154     },
45155     
45156     setStylesheets : function(stylesheets)
45157     {
45158         if(typeof(stylesheets) == 'string'){
45159             Roo.get(this.iframe.contentDocument.head).createChild({
45160                 tag : 'link',
45161                 rel : 'stylesheet',
45162                 type : 'text/css',
45163                 href : stylesheets
45164             });
45165             
45166             return;
45167         }
45168         var _this = this;
45169      
45170         Roo.each(stylesheets, function(s) {
45171             if(!s.length){
45172                 return;
45173             }
45174             
45175             Roo.get(_this.iframe.contentDocument.head).createChild({
45176                 tag : 'link',
45177                 rel : 'stylesheet',
45178                 type : 'text/css',
45179                 href : s
45180             });
45181         });
45182
45183         
45184     },
45185     
45186     removeStylesheets : function()
45187     {
45188         var _this = this;
45189         
45190         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
45191             s.remove();
45192         });
45193     },
45194     
45195     setStyle : function(style)
45196     {
45197         Roo.get(this.iframe.contentDocument.head).createChild({
45198             tag : 'style',
45199             type : 'text/css',
45200             html : style
45201         });
45202
45203         return;
45204     }
45205     
45206     // hide stuff that is not compatible
45207     /**
45208      * @event blur
45209      * @hide
45210      */
45211     /**
45212      * @event change
45213      * @hide
45214      */
45215     /**
45216      * @event focus
45217      * @hide
45218      */
45219     /**
45220      * @event specialkey
45221      * @hide
45222      */
45223     /**
45224      * @cfg {String} fieldClass @hide
45225      */
45226     /**
45227      * @cfg {String} focusClass @hide
45228      */
45229     /**
45230      * @cfg {String} autoCreate @hide
45231      */
45232     /**
45233      * @cfg {String} inputType @hide
45234      */
45235     /**
45236      * @cfg {String} invalidClass @hide
45237      */
45238     /**
45239      * @cfg {String} invalidText @hide
45240      */
45241     /**
45242      * @cfg {String} msgFx @hide
45243      */
45244     /**
45245      * @cfg {String} validateOnBlur @hide
45246      */
45247 });
45248
45249 Roo.HtmlEditorCore.white = [
45250         'area', 'br', 'img', 'input', 'hr', 'wbr',
45251         
45252        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
45253        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
45254        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
45255        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
45256        'table',   'ul',         'xmp', 
45257        
45258        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
45259       'thead',   'tr', 
45260      
45261       'dir', 'menu', 'ol', 'ul', 'dl',
45262        
45263       'embed',  'object'
45264 ];
45265
45266
45267 Roo.HtmlEditorCore.black = [
45268     //    'embed',  'object', // enable - backend responsiblity to clean thiese
45269         'applet', // 
45270         'base',   'basefont', 'bgsound', 'blink',  'body', 
45271         'frame',  'frameset', 'head',    'html',   'ilayer', 
45272         'iframe', 'layer',  'link',     'meta',    'object',   
45273         'script', 'style' ,'title',  'xml' // clean later..
45274 ];
45275 Roo.HtmlEditorCore.clean = [
45276     'script', 'style', 'title', 'xml'
45277 ];
45278 Roo.HtmlEditorCore.remove = [
45279     'font'
45280 ];
45281 // attributes..
45282
45283 Roo.HtmlEditorCore.ablack = [
45284     'on'
45285 ];
45286     
45287 Roo.HtmlEditorCore.aclean = [ 
45288     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
45289 ];
45290
45291 // protocols..
45292 Roo.HtmlEditorCore.pwhite= [
45293         'http',  'https',  'mailto'
45294 ];
45295
45296 // white listed style attributes.
45297 Roo.HtmlEditorCore.cwhite= [
45298       //  'text-align', /// default is to allow most things..
45299       
45300          
45301 //        'font-size'//??
45302 ];
45303
45304 // black listed style attributes.
45305 Roo.HtmlEditorCore.cblack= [
45306       //  'font-size' -- this can be set by the project 
45307 ];
45308
45309
45310 Roo.HtmlEditorCore.swapCodes   =[ 
45311     [    8211, "&#8211;" ], 
45312     [    8212, "&#8212;" ], 
45313     [    8216,  "'" ],  
45314     [    8217, "'" ],  
45315     [    8220, '"' ],  
45316     [    8221, '"' ],  
45317     [    8226, "*" ],  
45318     [    8230, "..." ]
45319 ]; 
45320
45321     //<script type="text/javascript">
45322
45323 /*
45324  * Ext JS Library 1.1.1
45325  * Copyright(c) 2006-2007, Ext JS, LLC.
45326  * Licence LGPL
45327  * 
45328  */
45329  
45330  
45331 Roo.form.HtmlEditor = function(config){
45332     
45333     
45334     
45335     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45336     
45337     if (!this.toolbars) {
45338         this.toolbars = [];
45339     }
45340     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45341     
45342     
45343 };
45344
45345 /**
45346  * @class Roo.form.HtmlEditor
45347  * @extends Roo.form.Field
45348  * Provides a lightweight HTML Editor component.
45349  *
45350  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45351  * 
45352  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45353  * supported by this editor.</b><br/><br/>
45354  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45355  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45356  */
45357 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45358     /**
45359      * @cfg {Boolean} clearUp
45360      */
45361     clearUp : true,
45362       /**
45363      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45364      */
45365     toolbars : false,
45366    
45367      /**
45368      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45369      *                        Roo.resizable.
45370      */
45371     resizable : false,
45372      /**
45373      * @cfg {Number} height (in pixels)
45374      */   
45375     height: 300,
45376    /**
45377      * @cfg {Number} width (in pixels)
45378      */   
45379     width: 500,
45380     
45381     /**
45382      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45383      * 
45384      */
45385     stylesheets: false,
45386     
45387     
45388      /**
45389      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45390      * 
45391      */
45392     cblack: false,
45393     /**
45394      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45395      * 
45396      */
45397     cwhite: false,
45398     
45399      /**
45400      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45401      * 
45402      */
45403     black: false,
45404     /**
45405      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45406      * 
45407      */
45408     white: false,
45409     
45410     // id of frame..
45411     frameId: false,
45412     
45413     // private properties
45414     validationEvent : false,
45415     deferHeight: true,
45416     initialized : false,
45417     activated : false,
45418     
45419     onFocus : Roo.emptyFn,
45420     iframePad:3,
45421     hideMode:'offsets',
45422     
45423     actionMode : 'container', // defaults to hiding it...
45424     
45425     defaultAutoCreate : { // modified by initCompnoent..
45426         tag: "textarea",
45427         style:"width:500px;height:300px;",
45428         autocomplete: "new-password"
45429     },
45430
45431     // private
45432     initComponent : function(){
45433         this.addEvents({
45434             /**
45435              * @event initialize
45436              * Fires when the editor is fully initialized (including the iframe)
45437              * @param {HtmlEditor} this
45438              */
45439             initialize: true,
45440             /**
45441              * @event activate
45442              * Fires when the editor is first receives the focus. Any insertion must wait
45443              * until after this event.
45444              * @param {HtmlEditor} this
45445              */
45446             activate: true,
45447              /**
45448              * @event beforesync
45449              * Fires before the textarea is updated with content from the editor iframe. Return false
45450              * to cancel the sync.
45451              * @param {HtmlEditor} this
45452              * @param {String} html
45453              */
45454             beforesync: true,
45455              /**
45456              * @event beforepush
45457              * Fires before the iframe editor is updated with content from the textarea. Return false
45458              * to cancel the push.
45459              * @param {HtmlEditor} this
45460              * @param {String} html
45461              */
45462             beforepush: true,
45463              /**
45464              * @event sync
45465              * Fires when the textarea is updated with content from the editor iframe.
45466              * @param {HtmlEditor} this
45467              * @param {String} html
45468              */
45469             sync: true,
45470              /**
45471              * @event push
45472              * Fires when the iframe editor is updated with content from the textarea.
45473              * @param {HtmlEditor} this
45474              * @param {String} html
45475              */
45476             push: true,
45477              /**
45478              * @event editmodechange
45479              * Fires when the editor switches edit modes
45480              * @param {HtmlEditor} this
45481              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45482              */
45483             editmodechange: true,
45484             /**
45485              * @event editorevent
45486              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45487              * @param {HtmlEditor} this
45488              */
45489             editorevent: true,
45490             /**
45491              * @event firstfocus
45492              * Fires when on first focus - needed by toolbars..
45493              * @param {HtmlEditor} this
45494              */
45495             firstfocus: true,
45496             /**
45497              * @event autosave
45498              * Auto save the htmlEditor value as a file into Events
45499              * @param {HtmlEditor} this
45500              */
45501             autosave: true,
45502             /**
45503              * @event savedpreview
45504              * preview the saved version of htmlEditor
45505              * @param {HtmlEditor} this
45506              */
45507             savedpreview: true,
45508             
45509             /**
45510             * @event stylesheetsclick
45511             * Fires when press the Sytlesheets button
45512             * @param {Roo.HtmlEditorCore} this
45513             */
45514             stylesheetsclick: true
45515         });
45516         this.defaultAutoCreate =  {
45517             tag: "textarea",
45518             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45519             autocomplete: "new-password"
45520         };
45521     },
45522
45523     /**
45524      * Protected method that will not generally be called directly. It
45525      * is called when the editor creates its toolbar. Override this method if you need to
45526      * add custom toolbar buttons.
45527      * @param {HtmlEditor} editor
45528      */
45529     createToolbar : function(editor){
45530         Roo.log("create toolbars");
45531         if (!editor.toolbars || !editor.toolbars.length) {
45532             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45533         }
45534         
45535         for (var i =0 ; i < editor.toolbars.length;i++) {
45536             editor.toolbars[i] = Roo.factory(
45537                     typeof(editor.toolbars[i]) == 'string' ?
45538                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45539                 Roo.form.HtmlEditor);
45540             editor.toolbars[i].init(editor);
45541         }
45542          
45543         
45544     },
45545
45546      
45547     // private
45548     onRender : function(ct, position)
45549     {
45550         var _t = this;
45551         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45552         
45553         this.wrap = this.el.wrap({
45554             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45555         });
45556         
45557         this.editorcore.onRender(ct, position);
45558          
45559         if (this.resizable) {
45560             this.resizeEl = new Roo.Resizable(this.wrap, {
45561                 pinned : true,
45562                 wrap: true,
45563                 dynamic : true,
45564                 minHeight : this.height,
45565                 height: this.height,
45566                 handles : this.resizable,
45567                 width: this.width,
45568                 listeners : {
45569                     resize : function(r, w, h) {
45570                         _t.onResize(w,h); // -something
45571                     }
45572                 }
45573             });
45574             
45575         }
45576         this.createToolbar(this);
45577        
45578         
45579         if(!this.width){
45580             this.setSize(this.wrap.getSize());
45581         }
45582         if (this.resizeEl) {
45583             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45584             // should trigger onReize..
45585         }
45586         
45587         this.keyNav = new Roo.KeyNav(this.el, {
45588             
45589             "tab" : function(e){
45590                 e.preventDefault();
45591                 
45592                 var value = this.getValue();
45593                 
45594                 var start = this.el.dom.selectionStart;
45595                 var end = this.el.dom.selectionEnd;
45596                 
45597                 if(!e.shiftKey){
45598                     
45599                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45600                     this.el.dom.setSelectionRange(end + 1, end + 1);
45601                     return;
45602                 }
45603                 
45604                 var f = value.substring(0, start).split("\t");
45605                 
45606                 if(f.pop().length != 0){
45607                     return;
45608                 }
45609                 
45610                 this.setValue(f.join("\t") + value.substring(end));
45611                 this.el.dom.setSelectionRange(start - 1, start - 1);
45612                 
45613             },
45614             
45615             "home" : function(e){
45616                 e.preventDefault();
45617                 
45618                 var curr = this.el.dom.selectionStart;
45619                 var lines = this.getValue().split("\n");
45620                 
45621                 if(!lines.length){
45622                     return;
45623                 }
45624                 
45625                 if(e.ctrlKey){
45626                     this.el.dom.setSelectionRange(0, 0);
45627                     return;
45628                 }
45629                 
45630                 var pos = 0;
45631                 
45632                 for (var i = 0; i < lines.length;i++) {
45633                     pos += lines[i].length;
45634                     
45635                     if(i != 0){
45636                         pos += 1;
45637                     }
45638                     
45639                     if(pos < curr){
45640                         continue;
45641                     }
45642                     
45643                     pos -= lines[i].length;
45644                     
45645                     break;
45646                 }
45647                 
45648                 if(!e.shiftKey){
45649                     this.el.dom.setSelectionRange(pos, pos);
45650                     return;
45651                 }
45652                 
45653                 this.el.dom.selectionStart = pos;
45654                 this.el.dom.selectionEnd = curr;
45655             },
45656             
45657             "end" : function(e){
45658                 e.preventDefault();
45659                 
45660                 var curr = this.el.dom.selectionStart;
45661                 var lines = this.getValue().split("\n");
45662                 
45663                 if(!lines.length){
45664                     return;
45665                 }
45666                 
45667                 if(e.ctrlKey){
45668                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45669                     return;
45670                 }
45671                 
45672                 var pos = 0;
45673                 
45674                 for (var i = 0; i < lines.length;i++) {
45675                     
45676                     pos += lines[i].length;
45677                     
45678                     if(i != 0){
45679                         pos += 1;
45680                     }
45681                     
45682                     if(pos < curr){
45683                         continue;
45684                     }
45685                     
45686                     break;
45687                 }
45688                 
45689                 if(!e.shiftKey){
45690                     this.el.dom.setSelectionRange(pos, pos);
45691                     return;
45692                 }
45693                 
45694                 this.el.dom.selectionStart = curr;
45695                 this.el.dom.selectionEnd = pos;
45696             },
45697
45698             scope : this,
45699
45700             doRelay : function(foo, bar, hname){
45701                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45702             },
45703
45704             forceKeyDown: true
45705         });
45706         
45707 //        if(this.autosave && this.w){
45708 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45709 //        }
45710     },
45711
45712     // private
45713     onResize : function(w, h)
45714     {
45715         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45716         var ew = false;
45717         var eh = false;
45718         
45719         if(this.el ){
45720             if(typeof w == 'number'){
45721                 var aw = w - this.wrap.getFrameWidth('lr');
45722                 this.el.setWidth(this.adjustWidth('textarea', aw));
45723                 ew = aw;
45724             }
45725             if(typeof h == 'number'){
45726                 var tbh = 0;
45727                 for (var i =0; i < this.toolbars.length;i++) {
45728                     // fixme - ask toolbars for heights?
45729                     tbh += this.toolbars[i].tb.el.getHeight();
45730                     if (this.toolbars[i].footer) {
45731                         tbh += this.toolbars[i].footer.el.getHeight();
45732                     }
45733                 }
45734                 
45735                 
45736                 
45737                 
45738                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45739                 ah -= 5; // knock a few pixes off for look..
45740 //                Roo.log(ah);
45741                 this.el.setHeight(this.adjustWidth('textarea', ah));
45742                 var eh = ah;
45743             }
45744         }
45745         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45746         this.editorcore.onResize(ew,eh);
45747         
45748     },
45749
45750     /**
45751      * Toggles the editor between standard and source edit mode.
45752      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45753      */
45754     toggleSourceEdit : function(sourceEditMode)
45755     {
45756         this.editorcore.toggleSourceEdit(sourceEditMode);
45757         
45758         if(this.editorcore.sourceEditMode){
45759             Roo.log('editor - showing textarea');
45760             
45761 //            Roo.log('in');
45762 //            Roo.log(this.syncValue());
45763             this.editorcore.syncValue();
45764             this.el.removeClass('x-hidden');
45765             this.el.dom.removeAttribute('tabIndex');
45766             this.el.focus();
45767             
45768             for (var i = 0; i < this.toolbars.length; i++) {
45769                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45770                     this.toolbars[i].tb.hide();
45771                     this.toolbars[i].footer.hide();
45772                 }
45773             }
45774             
45775         }else{
45776             Roo.log('editor - hiding textarea');
45777 //            Roo.log('out')
45778 //            Roo.log(this.pushValue()); 
45779             this.editorcore.pushValue();
45780             
45781             this.el.addClass('x-hidden');
45782             this.el.dom.setAttribute('tabIndex', -1);
45783             
45784             for (var i = 0; i < this.toolbars.length; i++) {
45785                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45786                     this.toolbars[i].tb.show();
45787                     this.toolbars[i].footer.show();
45788                 }
45789             }
45790             
45791             //this.deferFocus();
45792         }
45793         
45794         this.setSize(this.wrap.getSize());
45795         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45796         
45797         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45798     },
45799  
45800     // private (for BoxComponent)
45801     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45802
45803     // private (for BoxComponent)
45804     getResizeEl : function(){
45805         return this.wrap;
45806     },
45807
45808     // private (for BoxComponent)
45809     getPositionEl : function(){
45810         return this.wrap;
45811     },
45812
45813     // private
45814     initEvents : function(){
45815         this.originalValue = this.getValue();
45816     },
45817
45818     /**
45819      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45820      * @method
45821      */
45822     markInvalid : Roo.emptyFn,
45823     /**
45824      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45825      * @method
45826      */
45827     clearInvalid : Roo.emptyFn,
45828
45829     setValue : function(v){
45830         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45831         this.editorcore.pushValue();
45832     },
45833
45834      
45835     // private
45836     deferFocus : function(){
45837         this.focus.defer(10, this);
45838     },
45839
45840     // doc'ed in Field
45841     focus : function(){
45842         this.editorcore.focus();
45843         
45844     },
45845       
45846
45847     // private
45848     onDestroy : function(){
45849         
45850         
45851         
45852         if(this.rendered){
45853             
45854             for (var i =0; i < this.toolbars.length;i++) {
45855                 // fixme - ask toolbars for heights?
45856                 this.toolbars[i].onDestroy();
45857             }
45858             
45859             this.wrap.dom.innerHTML = '';
45860             this.wrap.remove();
45861         }
45862     },
45863
45864     // private
45865     onFirstFocus : function(){
45866         //Roo.log("onFirstFocus");
45867         this.editorcore.onFirstFocus();
45868          for (var i =0; i < this.toolbars.length;i++) {
45869             this.toolbars[i].onFirstFocus();
45870         }
45871         
45872     },
45873     
45874     // private
45875     syncValue : function()
45876     {
45877         this.editorcore.syncValue();
45878     },
45879     
45880     pushValue : function()
45881     {
45882         this.editorcore.pushValue();
45883     },
45884     
45885     setStylesheets : function(stylesheets)
45886     {
45887         this.editorcore.setStylesheets(stylesheets);
45888     },
45889     
45890     removeStylesheets : function()
45891     {
45892         this.editorcore.removeStylesheets();
45893     }
45894      
45895     
45896     // hide stuff that is not compatible
45897     /**
45898      * @event blur
45899      * @hide
45900      */
45901     /**
45902      * @event change
45903      * @hide
45904      */
45905     /**
45906      * @event focus
45907      * @hide
45908      */
45909     /**
45910      * @event specialkey
45911      * @hide
45912      */
45913     /**
45914      * @cfg {String} fieldClass @hide
45915      */
45916     /**
45917      * @cfg {String} focusClass @hide
45918      */
45919     /**
45920      * @cfg {String} autoCreate @hide
45921      */
45922     /**
45923      * @cfg {String} inputType @hide
45924      */
45925     /**
45926      * @cfg {String} invalidClass @hide
45927      */
45928     /**
45929      * @cfg {String} invalidText @hide
45930      */
45931     /**
45932      * @cfg {String} msgFx @hide
45933      */
45934     /**
45935      * @cfg {String} validateOnBlur @hide
45936      */
45937 });
45938  
45939     // <script type="text/javascript">
45940 /*
45941  * Based on
45942  * Ext JS Library 1.1.1
45943  * Copyright(c) 2006-2007, Ext JS, LLC.
45944  *  
45945  
45946  */
45947
45948 /**
45949  * @class Roo.form.HtmlEditorToolbar1
45950  * Basic Toolbar
45951  * 
45952  * Usage:
45953  *
45954  new Roo.form.HtmlEditor({
45955     ....
45956     toolbars : [
45957         new Roo.form.HtmlEditorToolbar1({
45958             disable : { fonts: 1 , format: 1, ..., ... , ...],
45959             btns : [ .... ]
45960         })
45961     }
45962      
45963  * 
45964  * @cfg {Object} disable List of elements to disable..
45965  * @cfg {Array} btns List of additional buttons.
45966  * 
45967  * 
45968  * NEEDS Extra CSS? 
45969  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45970  */
45971  
45972 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45973 {
45974     
45975     Roo.apply(this, config);
45976     
45977     // default disabled, based on 'good practice'..
45978     this.disable = this.disable || {};
45979     Roo.applyIf(this.disable, {
45980         fontSize : true,
45981         colors : true,
45982         specialElements : true
45983     });
45984     
45985     
45986     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45987     // dont call parent... till later.
45988 }
45989
45990 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45991     
45992     tb: false,
45993     
45994     rendered: false,
45995     
45996     editor : false,
45997     editorcore : false,
45998     /**
45999      * @cfg {Object} disable  List of toolbar elements to disable
46000          
46001      */
46002     disable : false,
46003     
46004     
46005      /**
46006      * @cfg {String} createLinkText The default text for the create link prompt
46007      */
46008     createLinkText : 'Please enter the URL for the link:',
46009     /**
46010      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
46011      */
46012     defaultLinkValue : 'http:/'+'/',
46013    
46014     
46015       /**
46016      * @cfg {Array} fontFamilies An array of available font families
46017      */
46018     fontFamilies : [
46019         'Arial',
46020         'Courier New',
46021         'Tahoma',
46022         'Times New Roman',
46023         'Verdana'
46024     ],
46025     
46026     specialChars : [
46027            "&#169;",
46028           "&#174;",     
46029           "&#8482;",    
46030           "&#163;" ,    
46031          // "&#8212;",    
46032           "&#8230;",    
46033           "&#247;" ,    
46034         //  "&#225;" ,     ?? a acute?
46035            "&#8364;"    , //Euro
46036        //   "&#8220;"    ,
46037         //  "&#8221;"    ,
46038         //  "&#8226;"    ,
46039           "&#176;"  //   , // degrees
46040
46041          // "&#233;"     , // e ecute
46042          // "&#250;"     , // u ecute?
46043     ],
46044     
46045     specialElements : [
46046         {
46047             text: "Insert Table",
46048             xtype: 'MenuItem',
46049             xns : Roo.Menu,
46050             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
46051                 
46052         },
46053         {    
46054             text: "Insert Image",
46055             xtype: 'MenuItem',
46056             xns : Roo.Menu,
46057             ihtml : '<img src="about:blank"/>'
46058             
46059         }
46060         
46061          
46062     ],
46063     
46064     
46065     inputElements : [ 
46066             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
46067             "input:submit", "input:button", "select", "textarea", "label" ],
46068     formats : [
46069         ["p"] ,  
46070         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
46071         ["pre"],[ "code"], 
46072         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
46073         ['div'],['span'],
46074         ['sup'],['sub']
46075     ],
46076     
46077     cleanStyles : [
46078         "font-size"
46079     ],
46080      /**
46081      * @cfg {String} defaultFont default font to use.
46082      */
46083     defaultFont: 'tahoma',
46084    
46085     fontSelect : false,
46086     
46087     
46088     formatCombo : false,
46089     
46090     init : function(editor)
46091     {
46092         this.editor = editor;
46093         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46094         var editorcore = this.editorcore;
46095         
46096         var _t = this;
46097         
46098         var fid = editorcore.frameId;
46099         var etb = this;
46100         function btn(id, toggle, handler){
46101             var xid = fid + '-'+ id ;
46102             return {
46103                 id : xid,
46104                 cmd : id,
46105                 cls : 'x-btn-icon x-edit-'+id,
46106                 enableToggle:toggle !== false,
46107                 scope: _t, // was editor...
46108                 handler:handler||_t.relayBtnCmd,
46109                 clickEvent:'mousedown',
46110                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46111                 tabIndex:-1
46112             };
46113         }
46114         
46115         
46116         
46117         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46118         this.tb = tb;
46119          // stop form submits
46120         tb.el.on('click', function(e){
46121             e.preventDefault(); // what does this do?
46122         });
46123
46124         if(!this.disable.font) { // && !Roo.isSafari){
46125             /* why no safari for fonts 
46126             editor.fontSelect = tb.el.createChild({
46127                 tag:'select',
46128                 tabIndex: -1,
46129                 cls:'x-font-select',
46130                 html: this.createFontOptions()
46131             });
46132             
46133             editor.fontSelect.on('change', function(){
46134                 var font = editor.fontSelect.dom.value;
46135                 editor.relayCmd('fontname', font);
46136                 editor.deferFocus();
46137             }, editor);
46138             
46139             tb.add(
46140                 editor.fontSelect.dom,
46141                 '-'
46142             );
46143             */
46144             
46145         };
46146         if(!this.disable.formats){
46147             this.formatCombo = new Roo.form.ComboBox({
46148                 store: new Roo.data.SimpleStore({
46149                     id : 'tag',
46150                     fields: ['tag'],
46151                     data : this.formats // from states.js
46152                 }),
46153                 blockFocus : true,
46154                 name : '',
46155                 //autoCreate : {tag: "div",  size: "20"},
46156                 displayField:'tag',
46157                 typeAhead: false,
46158                 mode: 'local',
46159                 editable : false,
46160                 triggerAction: 'all',
46161                 emptyText:'Add tag',
46162                 selectOnFocus:true,
46163                 width:135,
46164                 listeners : {
46165                     'select': function(c, r, i) {
46166                         editorcore.insertTag(r.get('tag'));
46167                         editor.focus();
46168                     }
46169                 }
46170
46171             });
46172             tb.addField(this.formatCombo);
46173             
46174         }
46175         
46176         if(!this.disable.format){
46177             tb.add(
46178                 btn('bold'),
46179                 btn('italic'),
46180                 btn('underline'),
46181                 btn('strikethrough')
46182             );
46183         };
46184         if(!this.disable.fontSize){
46185             tb.add(
46186                 '-',
46187                 
46188                 
46189                 btn('increasefontsize', false, editorcore.adjustFont),
46190                 btn('decreasefontsize', false, editorcore.adjustFont)
46191             );
46192         };
46193         
46194         
46195         if(!this.disable.colors){
46196             tb.add(
46197                 '-', {
46198                     id:editorcore.frameId +'-forecolor',
46199                     cls:'x-btn-icon x-edit-forecolor',
46200                     clickEvent:'mousedown',
46201                     tooltip: this.buttonTips['forecolor'] || undefined,
46202                     tabIndex:-1,
46203                     menu : new Roo.menu.ColorMenu({
46204                         allowReselect: true,
46205                         focus: Roo.emptyFn,
46206                         value:'000000',
46207                         plain:true,
46208                         selectHandler: function(cp, color){
46209                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
46210                             editor.deferFocus();
46211                         },
46212                         scope: editorcore,
46213                         clickEvent:'mousedown'
46214                     })
46215                 }, {
46216                     id:editorcore.frameId +'backcolor',
46217                     cls:'x-btn-icon x-edit-backcolor',
46218                     clickEvent:'mousedown',
46219                     tooltip: this.buttonTips['backcolor'] || undefined,
46220                     tabIndex:-1,
46221                     menu : new Roo.menu.ColorMenu({
46222                         focus: Roo.emptyFn,
46223                         value:'FFFFFF',
46224                         plain:true,
46225                         allowReselect: true,
46226                         selectHandler: function(cp, color){
46227                             if(Roo.isGecko){
46228                                 editorcore.execCmd('useCSS', false);
46229                                 editorcore.execCmd('hilitecolor', color);
46230                                 editorcore.execCmd('useCSS', true);
46231                                 editor.deferFocus();
46232                             }else{
46233                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
46234                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
46235                                 editor.deferFocus();
46236                             }
46237                         },
46238                         scope:editorcore,
46239                         clickEvent:'mousedown'
46240                     })
46241                 }
46242             );
46243         };
46244         // now add all the items...
46245         
46246
46247         if(!this.disable.alignments){
46248             tb.add(
46249                 '-',
46250                 btn('justifyleft'),
46251                 btn('justifycenter'),
46252                 btn('justifyright')
46253             );
46254         };
46255
46256         //if(!Roo.isSafari){
46257             if(!this.disable.links){
46258                 tb.add(
46259                     '-',
46260                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
46261                 );
46262             };
46263
46264             if(!this.disable.lists){
46265                 tb.add(
46266                     '-',
46267                     btn('insertorderedlist'),
46268                     btn('insertunorderedlist')
46269                 );
46270             }
46271             if(!this.disable.sourceEdit){
46272                 tb.add(
46273                     '-',
46274                     btn('sourceedit', true, function(btn){
46275                         this.toggleSourceEdit(btn.pressed);
46276                     })
46277                 );
46278             }
46279         //}
46280         
46281         var smenu = { };
46282         // special menu.. - needs to be tidied up..
46283         if (!this.disable.special) {
46284             smenu = {
46285                 text: "&#169;",
46286                 cls: 'x-edit-none',
46287                 
46288                 menu : {
46289                     items : []
46290                 }
46291             };
46292             for (var i =0; i < this.specialChars.length; i++) {
46293                 smenu.menu.items.push({
46294                     
46295                     html: this.specialChars[i],
46296                     handler: function(a,b) {
46297                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
46298                         //editor.insertAtCursor(a.html);
46299                         
46300                     },
46301                     tabIndex:-1
46302                 });
46303             }
46304             
46305             
46306             tb.add(smenu);
46307             
46308             
46309         }
46310         
46311         var cmenu = { };
46312         if (!this.disable.cleanStyles) {
46313             cmenu = {
46314                 cls: 'x-btn-icon x-btn-clear',
46315                 
46316                 menu : {
46317                     items : []
46318                 }
46319             };
46320             for (var i =0; i < this.cleanStyles.length; i++) {
46321                 cmenu.menu.items.push({
46322                     actiontype : this.cleanStyles[i],
46323                     html: 'Remove ' + this.cleanStyles[i],
46324                     handler: function(a,b) {
46325 //                        Roo.log(a);
46326 //                        Roo.log(b);
46327                         var c = Roo.get(editorcore.doc.body);
46328                         c.select('[style]').each(function(s) {
46329                             s.dom.style.removeProperty(a.actiontype);
46330                         });
46331                         editorcore.syncValue();
46332                     },
46333                     tabIndex:-1
46334                 });
46335             }
46336              cmenu.menu.items.push({
46337                 actiontype : 'tablewidths',
46338                 html: 'Remove Table Widths',
46339                 handler: function(a,b) {
46340                     editorcore.cleanTableWidths();
46341                     editorcore.syncValue();
46342                 },
46343                 tabIndex:-1
46344             });
46345             cmenu.menu.items.push({
46346                 actiontype : 'word',
46347                 html: 'Remove MS Word Formating',
46348                 handler: function(a,b) {
46349                     editorcore.cleanWord();
46350                     editorcore.syncValue();
46351                 },
46352                 tabIndex:-1
46353             });
46354             
46355             cmenu.menu.items.push({
46356                 actiontype : 'all',
46357                 html: 'Remove All Styles',
46358                 handler: function(a,b) {
46359                     
46360                     var c = Roo.get(editorcore.doc.body);
46361                     c.select('[style]').each(function(s) {
46362                         s.dom.removeAttribute('style');
46363                     });
46364                     editorcore.syncValue();
46365                 },
46366                 tabIndex:-1
46367             });
46368             
46369             cmenu.menu.items.push({
46370                 actiontype : 'all',
46371                 html: 'Remove All CSS Classes',
46372                 handler: function(a,b) {
46373                     
46374                     var c = Roo.get(editorcore.doc.body);
46375                     c.select('[class]').each(function(s) {
46376                         s.dom.removeAttribute('class');
46377                     });
46378                     editorcore.cleanWord();
46379                     editorcore.syncValue();
46380                 },
46381                 tabIndex:-1
46382             });
46383             
46384              cmenu.menu.items.push({
46385                 actiontype : 'tidy',
46386                 html: 'Tidy HTML Source',
46387                 handler: function(a,b) {
46388                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46389                     editorcore.syncValue();
46390                 },
46391                 tabIndex:-1
46392             });
46393             
46394             
46395             tb.add(cmenu);
46396         }
46397          
46398         if (!this.disable.specialElements) {
46399             var semenu = {
46400                 text: "Other;",
46401                 cls: 'x-edit-none',
46402                 menu : {
46403                     items : []
46404                 }
46405             };
46406             for (var i =0; i < this.specialElements.length; i++) {
46407                 semenu.menu.items.push(
46408                     Roo.apply({ 
46409                         handler: function(a,b) {
46410                             editor.insertAtCursor(this.ihtml);
46411                         }
46412                     }, this.specialElements[i])
46413                 );
46414                     
46415             }
46416             
46417             tb.add(semenu);
46418             
46419             
46420         }
46421          
46422         
46423         if (this.btns) {
46424             for(var i =0; i< this.btns.length;i++) {
46425                 var b = Roo.factory(this.btns[i],Roo.form);
46426                 b.cls =  'x-edit-none';
46427                 
46428                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46429                     b.cls += ' x-init-enable';
46430                 }
46431                 
46432                 b.scope = editorcore;
46433                 tb.add(b);
46434             }
46435         
46436         }
46437         
46438         
46439         
46440         // disable everything...
46441         
46442         this.tb.items.each(function(item){
46443             
46444            if(
46445                 item.id != editorcore.frameId+ '-sourceedit' && 
46446                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46447             ){
46448                 
46449                 item.disable();
46450             }
46451         });
46452         this.rendered = true;
46453         
46454         // the all the btns;
46455         editor.on('editorevent', this.updateToolbar, this);
46456         // other toolbars need to implement this..
46457         //editor.on('editmodechange', this.updateToolbar, this);
46458     },
46459     
46460     
46461     relayBtnCmd : function(btn) {
46462         this.editorcore.relayCmd(btn.cmd);
46463     },
46464     // private used internally
46465     createLink : function(){
46466         Roo.log("create link?");
46467         var url = prompt(this.createLinkText, this.defaultLinkValue);
46468         if(url && url != 'http:/'+'/'){
46469             this.editorcore.relayCmd('createlink', url);
46470         }
46471     },
46472
46473     
46474     /**
46475      * Protected method that will not generally be called directly. It triggers
46476      * a toolbar update by reading the markup state of the current selection in the editor.
46477      */
46478     updateToolbar: function(){
46479
46480         if(!this.editorcore.activated){
46481             this.editor.onFirstFocus();
46482             return;
46483         }
46484
46485         var btns = this.tb.items.map, 
46486             doc = this.editorcore.doc,
46487             frameId = this.editorcore.frameId;
46488
46489         if(!this.disable.font && !Roo.isSafari){
46490             /*
46491             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46492             if(name != this.fontSelect.dom.value){
46493                 this.fontSelect.dom.value = name;
46494             }
46495             */
46496         }
46497         if(!this.disable.format){
46498             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46499             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46500             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46501             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46502         }
46503         if(!this.disable.alignments){
46504             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46505             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46506             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46507         }
46508         if(!Roo.isSafari && !this.disable.lists){
46509             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46510             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46511         }
46512         
46513         var ans = this.editorcore.getAllAncestors();
46514         if (this.formatCombo) {
46515             
46516             
46517             var store = this.formatCombo.store;
46518             this.formatCombo.setValue("");
46519             for (var i =0; i < ans.length;i++) {
46520                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46521                     // select it..
46522                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46523                     break;
46524                 }
46525             }
46526         }
46527         
46528         
46529         
46530         // hides menus... - so this cant be on a menu...
46531         Roo.menu.MenuMgr.hideAll();
46532
46533         //this.editorsyncValue();
46534     },
46535    
46536     
46537     createFontOptions : function(){
46538         var buf = [], fs = this.fontFamilies, ff, lc;
46539         
46540         
46541         
46542         for(var i = 0, len = fs.length; i< len; i++){
46543             ff = fs[i];
46544             lc = ff.toLowerCase();
46545             buf.push(
46546                 '<option value="',lc,'" style="font-family:',ff,';"',
46547                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46548                     ff,
46549                 '</option>'
46550             );
46551         }
46552         return buf.join('');
46553     },
46554     
46555     toggleSourceEdit : function(sourceEditMode){
46556         
46557         Roo.log("toolbar toogle");
46558         if(sourceEditMode === undefined){
46559             sourceEditMode = !this.sourceEditMode;
46560         }
46561         this.sourceEditMode = sourceEditMode === true;
46562         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46563         // just toggle the button?
46564         if(btn.pressed !== this.sourceEditMode){
46565             btn.toggle(this.sourceEditMode);
46566             return;
46567         }
46568         
46569         if(sourceEditMode){
46570             Roo.log("disabling buttons");
46571             this.tb.items.each(function(item){
46572                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46573                     item.disable();
46574                 }
46575             });
46576           
46577         }else{
46578             Roo.log("enabling buttons");
46579             if(this.editorcore.initialized){
46580                 this.tb.items.each(function(item){
46581                     item.enable();
46582                 });
46583             }
46584             
46585         }
46586         Roo.log("calling toggole on editor");
46587         // tell the editor that it's been pressed..
46588         this.editor.toggleSourceEdit(sourceEditMode);
46589        
46590     },
46591      /**
46592      * Object collection of toolbar tooltips for the buttons in the editor. The key
46593      * is the command id associated with that button and the value is a valid QuickTips object.
46594      * For example:
46595 <pre><code>
46596 {
46597     bold : {
46598         title: 'Bold (Ctrl+B)',
46599         text: 'Make the selected text bold.',
46600         cls: 'x-html-editor-tip'
46601     },
46602     italic : {
46603         title: 'Italic (Ctrl+I)',
46604         text: 'Make the selected text italic.',
46605         cls: 'x-html-editor-tip'
46606     },
46607     ...
46608 </code></pre>
46609     * @type Object
46610      */
46611     buttonTips : {
46612         bold : {
46613             title: 'Bold (Ctrl+B)',
46614             text: 'Make the selected text bold.',
46615             cls: 'x-html-editor-tip'
46616         },
46617         italic : {
46618             title: 'Italic (Ctrl+I)',
46619             text: 'Make the selected text italic.',
46620             cls: 'x-html-editor-tip'
46621         },
46622         underline : {
46623             title: 'Underline (Ctrl+U)',
46624             text: 'Underline the selected text.',
46625             cls: 'x-html-editor-tip'
46626         },
46627         strikethrough : {
46628             title: 'Strikethrough',
46629             text: 'Strikethrough the selected text.',
46630             cls: 'x-html-editor-tip'
46631         },
46632         increasefontsize : {
46633             title: 'Grow Text',
46634             text: 'Increase the font size.',
46635             cls: 'x-html-editor-tip'
46636         },
46637         decreasefontsize : {
46638             title: 'Shrink Text',
46639             text: 'Decrease the font size.',
46640             cls: 'x-html-editor-tip'
46641         },
46642         backcolor : {
46643             title: 'Text Highlight Color',
46644             text: 'Change the background color of the selected text.',
46645             cls: 'x-html-editor-tip'
46646         },
46647         forecolor : {
46648             title: 'Font Color',
46649             text: 'Change the color of the selected text.',
46650             cls: 'x-html-editor-tip'
46651         },
46652         justifyleft : {
46653             title: 'Align Text Left',
46654             text: 'Align text to the left.',
46655             cls: 'x-html-editor-tip'
46656         },
46657         justifycenter : {
46658             title: 'Center Text',
46659             text: 'Center text in the editor.',
46660             cls: 'x-html-editor-tip'
46661         },
46662         justifyright : {
46663             title: 'Align Text Right',
46664             text: 'Align text to the right.',
46665             cls: 'x-html-editor-tip'
46666         },
46667         insertunorderedlist : {
46668             title: 'Bullet List',
46669             text: 'Start a bulleted list.',
46670             cls: 'x-html-editor-tip'
46671         },
46672         insertorderedlist : {
46673             title: 'Numbered List',
46674             text: 'Start a numbered list.',
46675             cls: 'x-html-editor-tip'
46676         },
46677         createlink : {
46678             title: 'Hyperlink',
46679             text: 'Make the selected text a hyperlink.',
46680             cls: 'x-html-editor-tip'
46681         },
46682         sourceedit : {
46683             title: 'Source Edit',
46684             text: 'Switch to source editing mode.',
46685             cls: 'x-html-editor-tip'
46686         }
46687     },
46688     // private
46689     onDestroy : function(){
46690         if(this.rendered){
46691             
46692             this.tb.items.each(function(item){
46693                 if(item.menu){
46694                     item.menu.removeAll();
46695                     if(item.menu.el){
46696                         item.menu.el.destroy();
46697                     }
46698                 }
46699                 item.destroy();
46700             });
46701              
46702         }
46703     },
46704     onFirstFocus: function() {
46705         this.tb.items.each(function(item){
46706            item.enable();
46707         });
46708     }
46709 });
46710
46711
46712
46713
46714 // <script type="text/javascript">
46715 /*
46716  * Based on
46717  * Ext JS Library 1.1.1
46718  * Copyright(c) 2006-2007, Ext JS, LLC.
46719  *  
46720  
46721  */
46722
46723  
46724 /**
46725  * @class Roo.form.HtmlEditor.ToolbarContext
46726  * Context Toolbar
46727  * 
46728  * Usage:
46729  *
46730  new Roo.form.HtmlEditor({
46731     ....
46732     toolbars : [
46733         { xtype: 'ToolbarStandard', styles : {} }
46734         { xtype: 'ToolbarContext', disable : {} }
46735     ]
46736 })
46737
46738      
46739  * 
46740  * @config : {Object} disable List of elements to disable.. (not done yet.)
46741  * @config : {Object} styles  Map of styles available.
46742  * 
46743  */
46744
46745 Roo.form.HtmlEditor.ToolbarContext = function(config)
46746 {
46747     
46748     Roo.apply(this, config);
46749     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46750     // dont call parent... till later.
46751     this.styles = this.styles || {};
46752 }
46753
46754  
46755
46756 Roo.form.HtmlEditor.ToolbarContext.types = {
46757     'IMG' : {
46758         width : {
46759             title: "Width",
46760             width: 40
46761         },
46762         height:  {
46763             title: "Height",
46764             width: 40
46765         },
46766         align: {
46767             title: "Align",
46768             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46769             width : 80
46770             
46771         },
46772         border: {
46773             title: "Border",
46774             width: 40
46775         },
46776         alt: {
46777             title: "Alt",
46778             width: 120
46779         },
46780         src : {
46781             title: "Src",
46782             width: 220
46783         }
46784         
46785     },
46786     'A' : {
46787         name : {
46788             title: "Name",
46789             width: 50
46790         },
46791         target:  {
46792             title: "Target",
46793             width: 120
46794         },
46795         href:  {
46796             title: "Href",
46797             width: 220
46798         } // border?
46799         
46800     },
46801     'TABLE' : {
46802         rows : {
46803             title: "Rows",
46804             width: 20
46805         },
46806         cols : {
46807             title: "Cols",
46808             width: 20
46809         },
46810         width : {
46811             title: "Width",
46812             width: 40
46813         },
46814         height : {
46815             title: "Height",
46816             width: 40
46817         },
46818         border : {
46819             title: "Border",
46820             width: 20
46821         }
46822     },
46823     'TD' : {
46824         width : {
46825             title: "Width",
46826             width: 40
46827         },
46828         height : {
46829             title: "Height",
46830             width: 40
46831         },   
46832         align: {
46833             title: "Align",
46834             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46835             width: 80
46836         },
46837         valign: {
46838             title: "Valign",
46839             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46840             width: 80
46841         },
46842         colspan: {
46843             title: "Colspan",
46844             width: 20
46845             
46846         },
46847          'font-family'  : {
46848             title : "Font",
46849             style : 'fontFamily',
46850             displayField: 'display',
46851             optname : 'font-family',
46852             width: 140
46853         }
46854     },
46855     'INPUT' : {
46856         name : {
46857             title: "name",
46858             width: 120
46859         },
46860         value : {
46861             title: "Value",
46862             width: 120
46863         },
46864         width : {
46865             title: "Width",
46866             width: 40
46867         }
46868     },
46869     'LABEL' : {
46870         'for' : {
46871             title: "For",
46872             width: 120
46873         }
46874     },
46875     'TEXTAREA' : {
46876           name : {
46877             title: "name",
46878             width: 120
46879         },
46880         rows : {
46881             title: "Rows",
46882             width: 20
46883         },
46884         cols : {
46885             title: "Cols",
46886             width: 20
46887         }
46888     },
46889     'SELECT' : {
46890         name : {
46891             title: "name",
46892             width: 120
46893         },
46894         selectoptions : {
46895             title: "Options",
46896             width: 200
46897         }
46898     },
46899     
46900     // should we really allow this??
46901     // should this just be 
46902     'BODY' : {
46903         title : {
46904             title: "Title",
46905             width: 200,
46906             disabled : true
46907         }
46908     },
46909     'SPAN' : {
46910         'font-family'  : {
46911             title : "Font",
46912             style : 'fontFamily',
46913             displayField: 'display',
46914             optname : 'font-family',
46915             width: 140
46916         }
46917     },
46918     'DIV' : {
46919         'font-family'  : {
46920             title : "Font",
46921             style : 'fontFamily',
46922             displayField: 'display',
46923             optname : 'font-family',
46924             width: 140
46925         }
46926     },
46927      'P' : {
46928         'font-family'  : {
46929             title : "Font",
46930             style : 'fontFamily',
46931             displayField: 'display',
46932             optname : 'font-family',
46933             width: 140
46934         }
46935     },
46936     
46937     '*' : {
46938         // empty..
46939     }
46940
46941 };
46942
46943 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46944 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46945
46946 Roo.form.HtmlEditor.ToolbarContext.options = {
46947         'font-family'  : [ 
46948                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46949                 [ 'Courier New', 'Courier New'],
46950                 [ 'Tahoma', 'Tahoma'],
46951                 [ 'Times New Roman,serif', 'Times'],
46952                 [ 'Verdana','Verdana' ]
46953         ]
46954 };
46955
46956 // fixme - these need to be configurable..
46957  
46958
46959 //Roo.form.HtmlEditor.ToolbarContext.types
46960
46961
46962 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46963     
46964     tb: false,
46965     
46966     rendered: false,
46967     
46968     editor : false,
46969     editorcore : false,
46970     /**
46971      * @cfg {Object} disable  List of toolbar elements to disable
46972          
46973      */
46974     disable : false,
46975     /**
46976      * @cfg {Object} styles List of styles 
46977      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46978      *
46979      * These must be defined in the page, so they get rendered correctly..
46980      * .headline { }
46981      * TD.underline { }
46982      * 
46983      */
46984     styles : false,
46985     
46986     options: false,
46987     
46988     toolbars : false,
46989     
46990     init : function(editor)
46991     {
46992         this.editor = editor;
46993         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46994         var editorcore = this.editorcore;
46995         
46996         var fid = editorcore.frameId;
46997         var etb = this;
46998         function btn(id, toggle, handler){
46999             var xid = fid + '-'+ id ;
47000             return {
47001                 id : xid,
47002                 cmd : id,
47003                 cls : 'x-btn-icon x-edit-'+id,
47004                 enableToggle:toggle !== false,
47005                 scope: editorcore, // was editor...
47006                 handler:handler||editorcore.relayBtnCmd,
47007                 clickEvent:'mousedown',
47008                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47009                 tabIndex:-1
47010             };
47011         }
47012         // create a new element.
47013         var wdiv = editor.wrap.createChild({
47014                 tag: 'div'
47015             }, editor.wrap.dom.firstChild.nextSibling, true);
47016         
47017         // can we do this more than once??
47018         
47019          // stop form submits
47020       
47021  
47022         // disable everything...
47023         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47024         this.toolbars = {};
47025            
47026         for (var i in  ty) {
47027           
47028             this.toolbars[i] = this.buildToolbar(ty[i],i);
47029         }
47030         this.tb = this.toolbars.BODY;
47031         this.tb.el.show();
47032         this.buildFooter();
47033         this.footer.show();
47034         editor.on('hide', function( ) { this.footer.hide() }, this);
47035         editor.on('show', function( ) { this.footer.show() }, this);
47036         
47037          
47038         this.rendered = true;
47039         
47040         // the all the btns;
47041         editor.on('editorevent', this.updateToolbar, this);
47042         // other toolbars need to implement this..
47043         //editor.on('editmodechange', this.updateToolbar, this);
47044     },
47045     
47046     
47047     
47048     /**
47049      * Protected method that will not generally be called directly. It triggers
47050      * a toolbar update by reading the markup state of the current selection in the editor.
47051      *
47052      * Note you can force an update by calling on('editorevent', scope, false)
47053      */
47054     updateToolbar: function(editor,ev,sel){
47055
47056         //Roo.log(ev);
47057         // capture mouse up - this is handy for selecting images..
47058         // perhaps should go somewhere else...
47059         if(!this.editorcore.activated){
47060              this.editor.onFirstFocus();
47061             return;
47062         }
47063         
47064         
47065         
47066         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
47067         // selectNode - might want to handle IE?
47068         if (ev &&
47069             (ev.type == 'mouseup' || ev.type == 'click' ) &&
47070             ev.target && ev.target.tagName == 'IMG') {
47071             // they have click on an image...
47072             // let's see if we can change the selection...
47073             sel = ev.target;
47074          
47075               var nodeRange = sel.ownerDocument.createRange();
47076             try {
47077                 nodeRange.selectNode(sel);
47078             } catch (e) {
47079                 nodeRange.selectNodeContents(sel);
47080             }
47081             //nodeRange.collapse(true);
47082             var s = this.editorcore.win.getSelection();
47083             s.removeAllRanges();
47084             s.addRange(nodeRange);
47085         }  
47086         
47087       
47088         var updateFooter = sel ? false : true;
47089         
47090         
47091         var ans = this.editorcore.getAllAncestors();
47092         
47093         // pick
47094         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47095         
47096         if (!sel) { 
47097             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
47098             sel = sel ? sel : this.editorcore.doc.body;
47099             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
47100             
47101         }
47102         // pick a menu that exists..
47103         var tn = sel.tagName.toUpperCase();
47104         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
47105         
47106         tn = sel.tagName.toUpperCase();
47107         
47108         var lastSel = this.tb.selectedNode;
47109         
47110         this.tb.selectedNode = sel;
47111         
47112         // if current menu does not match..
47113         
47114         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
47115                 
47116             this.tb.el.hide();
47117             ///console.log("show: " + tn);
47118             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
47119             this.tb.el.show();
47120             // update name
47121             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
47122             
47123             
47124             // update attributes
47125             if (this.tb.fields) {
47126                 this.tb.fields.each(function(e) {
47127                     if (e.stylename) {
47128                         e.setValue(sel.style[e.stylename]);
47129                         return;
47130                     } 
47131                    e.setValue(sel.getAttribute(e.attrname));
47132                 });
47133             }
47134             
47135             var hasStyles = false;
47136             for(var i in this.styles) {
47137                 hasStyles = true;
47138                 break;
47139             }
47140             
47141             // update styles
47142             if (hasStyles) { 
47143                 var st = this.tb.fields.item(0);
47144                 
47145                 st.store.removeAll();
47146                
47147                 
47148                 var cn = sel.className.split(/\s+/);
47149                 
47150                 var avs = [];
47151                 if (this.styles['*']) {
47152                     
47153                     Roo.each(this.styles['*'], function(v) {
47154                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47155                     });
47156                 }
47157                 if (this.styles[tn]) { 
47158                     Roo.each(this.styles[tn], function(v) {
47159                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47160                     });
47161                 }
47162                 
47163                 st.store.loadData(avs);
47164                 st.collapse();
47165                 st.setValue(cn);
47166             }
47167             // flag our selected Node.
47168             this.tb.selectedNode = sel;
47169            
47170            
47171             Roo.menu.MenuMgr.hideAll();
47172
47173         }
47174         
47175         if (!updateFooter) {
47176             //this.footDisp.dom.innerHTML = ''; 
47177             return;
47178         }
47179         // update the footer
47180         //
47181         var html = '';
47182         
47183         this.footerEls = ans.reverse();
47184         Roo.each(this.footerEls, function(a,i) {
47185             if (!a) { return; }
47186             html += html.length ? ' &gt; '  :  '';
47187             
47188             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
47189             
47190         });
47191        
47192         // 
47193         var sz = this.footDisp.up('td').getSize();
47194         this.footDisp.dom.style.width = (sz.width -10) + 'px';
47195         this.footDisp.dom.style.marginLeft = '5px';
47196         
47197         this.footDisp.dom.style.overflow = 'hidden';
47198         
47199         this.footDisp.dom.innerHTML = html;
47200             
47201         //this.editorsyncValue();
47202     },
47203      
47204     
47205    
47206        
47207     // private
47208     onDestroy : function(){
47209         if(this.rendered){
47210             
47211             this.tb.items.each(function(item){
47212                 if(item.menu){
47213                     item.menu.removeAll();
47214                     if(item.menu.el){
47215                         item.menu.el.destroy();
47216                     }
47217                 }
47218                 item.destroy();
47219             });
47220              
47221         }
47222     },
47223     onFirstFocus: function() {
47224         // need to do this for all the toolbars..
47225         this.tb.items.each(function(item){
47226            item.enable();
47227         });
47228     },
47229     buildToolbar: function(tlist, nm)
47230     {
47231         var editor = this.editor;
47232         var editorcore = this.editorcore;
47233          // create a new element.
47234         var wdiv = editor.wrap.createChild({
47235                 tag: 'div'
47236             }, editor.wrap.dom.firstChild.nextSibling, true);
47237         
47238        
47239         var tb = new Roo.Toolbar(wdiv);
47240         // add the name..
47241         
47242         tb.add(nm+ ":&nbsp;");
47243         
47244         var styles = [];
47245         for(var i in this.styles) {
47246             styles.push(i);
47247         }
47248         
47249         // styles...
47250         if (styles && styles.length) {
47251             
47252             // this needs a multi-select checkbox...
47253             tb.addField( new Roo.form.ComboBox({
47254                 store: new Roo.data.SimpleStore({
47255                     id : 'val',
47256                     fields: ['val', 'selected'],
47257                     data : [] 
47258                 }),
47259                 name : '-roo-edit-className',
47260                 attrname : 'className',
47261                 displayField: 'val',
47262                 typeAhead: false,
47263                 mode: 'local',
47264                 editable : false,
47265                 triggerAction: 'all',
47266                 emptyText:'Select Style',
47267                 selectOnFocus:true,
47268                 width: 130,
47269                 listeners : {
47270                     'select': function(c, r, i) {
47271                         // initial support only for on class per el..
47272                         tb.selectedNode.className =  r ? r.get('val') : '';
47273                         editorcore.syncValue();
47274                     }
47275                 }
47276     
47277             }));
47278         }
47279         
47280         var tbc = Roo.form.HtmlEditor.ToolbarContext;
47281         var tbops = tbc.options;
47282         
47283         for (var i in tlist) {
47284             
47285             var item = tlist[i];
47286             tb.add(item.title + ":&nbsp;");
47287             
47288             
47289             //optname == used so you can configure the options available..
47290             var opts = item.opts ? item.opts : false;
47291             if (item.optname) {
47292                 opts = tbops[item.optname];
47293            
47294             }
47295             
47296             if (opts) {
47297                 // opts == pulldown..
47298                 tb.addField( new Roo.form.ComboBox({
47299                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
47300                         id : 'val',
47301                         fields: ['val', 'display'],
47302                         data : opts  
47303                     }),
47304                     name : '-roo-edit-' + i,
47305                     attrname : i,
47306                     stylename : item.style ? item.style : false,
47307                     displayField: item.displayField ? item.displayField : 'val',
47308                     valueField :  'val',
47309                     typeAhead: false,
47310                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
47311                     editable : false,
47312                     triggerAction: 'all',
47313                     emptyText:'Select',
47314                     selectOnFocus:true,
47315                     width: item.width ? item.width  : 130,
47316                     listeners : {
47317                         'select': function(c, r, i) {
47318                             if (c.stylename) {
47319                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47320                                 return;
47321                             }
47322                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47323                         }
47324                     }
47325
47326                 }));
47327                 continue;
47328                     
47329                  
47330                 
47331                 tb.addField( new Roo.form.TextField({
47332                     name: i,
47333                     width: 100,
47334                     //allowBlank:false,
47335                     value: ''
47336                 }));
47337                 continue;
47338             }
47339             tb.addField( new Roo.form.TextField({
47340                 name: '-roo-edit-' + i,
47341                 attrname : i,
47342                 
47343                 width: item.width,
47344                 //allowBlank:true,
47345                 value: '',
47346                 listeners: {
47347                     'change' : function(f, nv, ov) {
47348                         tb.selectedNode.setAttribute(f.attrname, nv);
47349                         editorcore.syncValue();
47350                     }
47351                 }
47352             }));
47353              
47354         }
47355         
47356         var _this = this;
47357         
47358         if(nm == 'BODY'){
47359             tb.addSeparator();
47360         
47361             tb.addButton( {
47362                 text: 'Stylesheets',
47363
47364                 listeners : {
47365                     click : function ()
47366                     {
47367                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47368                     }
47369                 }
47370             });
47371         }
47372         
47373         tb.addFill();
47374         tb.addButton( {
47375             text: 'Remove Tag',
47376     
47377             listeners : {
47378                 click : function ()
47379                 {
47380                     // remove
47381                     // undo does not work.
47382                      
47383                     var sn = tb.selectedNode;
47384                     
47385                     var pn = sn.parentNode;
47386                     
47387                     var stn =  sn.childNodes[0];
47388                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47389                     while (sn.childNodes.length) {
47390                         var node = sn.childNodes[0];
47391                         sn.removeChild(node);
47392                         //Roo.log(node);
47393                         pn.insertBefore(node, sn);
47394                         
47395                     }
47396                     pn.removeChild(sn);
47397                     var range = editorcore.createRange();
47398         
47399                     range.setStart(stn,0);
47400                     range.setEnd(en,0); //????
47401                     //range.selectNode(sel);
47402                     
47403                     
47404                     var selection = editorcore.getSelection();
47405                     selection.removeAllRanges();
47406                     selection.addRange(range);
47407                     
47408                     
47409                     
47410                     //_this.updateToolbar(null, null, pn);
47411                     _this.updateToolbar(null, null, null);
47412                     _this.footDisp.dom.innerHTML = ''; 
47413                 }
47414             }
47415             
47416                     
47417                 
47418             
47419         });
47420         
47421         
47422         tb.el.on('click', function(e){
47423             e.preventDefault(); // what does this do?
47424         });
47425         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47426         tb.el.hide();
47427         tb.name = nm;
47428         // dont need to disable them... as they will get hidden
47429         return tb;
47430          
47431         
47432     },
47433     buildFooter : function()
47434     {
47435         
47436         var fel = this.editor.wrap.createChild();
47437         this.footer = new Roo.Toolbar(fel);
47438         // toolbar has scrolly on left / right?
47439         var footDisp= new Roo.Toolbar.Fill();
47440         var _t = this;
47441         this.footer.add(
47442             {
47443                 text : '&lt;',
47444                 xtype: 'Button',
47445                 handler : function() {
47446                     _t.footDisp.scrollTo('left',0,true)
47447                 }
47448             }
47449         );
47450         this.footer.add( footDisp );
47451         this.footer.add( 
47452             {
47453                 text : '&gt;',
47454                 xtype: 'Button',
47455                 handler : function() {
47456                     // no animation..
47457                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47458                 }
47459             }
47460         );
47461         var fel = Roo.get(footDisp.el);
47462         fel.addClass('x-editor-context');
47463         this.footDispWrap = fel; 
47464         this.footDispWrap.overflow  = 'hidden';
47465         
47466         this.footDisp = fel.createChild();
47467         this.footDispWrap.on('click', this.onContextClick, this)
47468         
47469         
47470     },
47471     onContextClick : function (ev,dom)
47472     {
47473         ev.preventDefault();
47474         var  cn = dom.className;
47475         //Roo.log(cn);
47476         if (!cn.match(/x-ed-loc-/)) {
47477             return;
47478         }
47479         var n = cn.split('-').pop();
47480         var ans = this.footerEls;
47481         var sel = ans[n];
47482         
47483          // pick
47484         var range = this.editorcore.createRange();
47485         
47486         range.selectNodeContents(sel);
47487         //range.selectNode(sel);
47488         
47489         
47490         var selection = this.editorcore.getSelection();
47491         selection.removeAllRanges();
47492         selection.addRange(range);
47493         
47494         
47495         
47496         this.updateToolbar(null, null, sel);
47497         
47498         
47499     }
47500     
47501     
47502     
47503     
47504     
47505 });
47506
47507
47508
47509
47510
47511 /*
47512  * Based on:
47513  * Ext JS Library 1.1.1
47514  * Copyright(c) 2006-2007, Ext JS, LLC.
47515  *
47516  * Originally Released Under LGPL - original licence link has changed is not relivant.
47517  *
47518  * Fork - LGPL
47519  * <script type="text/javascript">
47520  */
47521  
47522 /**
47523  * @class Roo.form.BasicForm
47524  * @extends Roo.util.Observable
47525  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47526  * @constructor
47527  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47528  * @param {Object} config Configuration options
47529  */
47530 Roo.form.BasicForm = function(el, config){
47531     this.allItems = [];
47532     this.childForms = [];
47533     Roo.apply(this, config);
47534     /*
47535      * The Roo.form.Field items in this form.
47536      * @type MixedCollection
47537      */
47538      
47539      
47540     this.items = new Roo.util.MixedCollection(false, function(o){
47541         return o.id || (o.id = Roo.id());
47542     });
47543     this.addEvents({
47544         /**
47545          * @event beforeaction
47546          * Fires before any action is performed. Return false to cancel the action.
47547          * @param {Form} this
47548          * @param {Action} action The action to be performed
47549          */
47550         beforeaction: true,
47551         /**
47552          * @event actionfailed
47553          * Fires when an action fails.
47554          * @param {Form} this
47555          * @param {Action} action The action that failed
47556          */
47557         actionfailed : true,
47558         /**
47559          * @event actioncomplete
47560          * Fires when an action is completed.
47561          * @param {Form} this
47562          * @param {Action} action The action that completed
47563          */
47564         actioncomplete : true
47565     });
47566     if(el){
47567         this.initEl(el);
47568     }
47569     Roo.form.BasicForm.superclass.constructor.call(this);
47570     
47571     Roo.form.BasicForm.popover.apply();
47572 };
47573
47574 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47575     /**
47576      * @cfg {String} method
47577      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47578      */
47579     /**
47580      * @cfg {DataReader} reader
47581      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47582      * This is optional as there is built-in support for processing JSON.
47583      */
47584     /**
47585      * @cfg {DataReader} errorReader
47586      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47587      * This is completely optional as there is built-in support for processing JSON.
47588      */
47589     /**
47590      * @cfg {String} url
47591      * The URL to use for form actions if one isn't supplied in the action options.
47592      */
47593     /**
47594      * @cfg {Boolean} fileUpload
47595      * Set to true if this form is a file upload.
47596      */
47597      
47598     /**
47599      * @cfg {Object} baseParams
47600      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47601      */
47602      /**
47603      
47604     /**
47605      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47606      */
47607     timeout: 30,
47608
47609     // private
47610     activeAction : null,
47611
47612     /**
47613      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47614      * or setValues() data instead of when the form was first created.
47615      */
47616     trackResetOnLoad : false,
47617     
47618     
47619     /**
47620      * childForms - used for multi-tab forms
47621      * @type {Array}
47622      */
47623     childForms : false,
47624     
47625     /**
47626      * allItems - full list of fields.
47627      * @type {Array}
47628      */
47629     allItems : false,
47630     
47631     /**
47632      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47633      * element by passing it or its id or mask the form itself by passing in true.
47634      * @type Mixed
47635      */
47636     waitMsgTarget : false,
47637     
47638     /**
47639      * @type Boolean
47640      */
47641     disableMask : false,
47642     
47643     /**
47644      * @cfg {Boolean} errorMask (true|false) default false
47645      */
47646     errorMask : false,
47647     
47648     /**
47649      * @cfg {Number} maskOffset Default 100
47650      */
47651     maskOffset : 100,
47652
47653     // private
47654     initEl : function(el){
47655         this.el = Roo.get(el);
47656         this.id = this.el.id || Roo.id();
47657         this.el.on('submit', this.onSubmit, this);
47658         this.el.addClass('x-form');
47659     },
47660
47661     // private
47662     onSubmit : function(e){
47663         e.stopEvent();
47664     },
47665
47666     /**
47667      * Returns true if client-side validation on the form is successful.
47668      * @return Boolean
47669      */
47670     isValid : function(){
47671         var valid = true;
47672         var target = false;
47673         this.items.each(function(f){
47674             if(f.validate()){
47675                 return;
47676             }
47677             
47678             valid = false;
47679                 
47680             if(!target && f.el.isVisible(true)){
47681                 target = f;
47682             }
47683         });
47684         
47685         if(this.errorMask && !valid){
47686             Roo.form.BasicForm.popover.mask(this, target);
47687         }
47688         
47689         return valid;
47690     },
47691     /**
47692      * Returns array of invalid form fields.
47693      * @return Array
47694      */
47695     
47696     invalidFields : function()
47697     {
47698         var ret = [];
47699         this.items.each(function(f){
47700             if(f.validate()){
47701                 return;
47702             }
47703             ret.push(f);
47704             
47705         });
47706         
47707         return ret;
47708     },
47709     
47710     
47711     /**
47712      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47713      * @return Boolean
47714      */
47715     isDirty : function(){
47716         var dirty = false;
47717         this.items.each(function(f){
47718            if(f.isDirty()){
47719                dirty = true;
47720                return false;
47721            }
47722         });
47723         return dirty;
47724     },
47725     
47726     /**
47727      * Returns true if any fields in this form have changed since their original load. (New version)
47728      * @return Boolean
47729      */
47730     
47731     hasChanged : function()
47732     {
47733         var dirty = false;
47734         this.items.each(function(f){
47735            if(f.hasChanged()){
47736                dirty = true;
47737                return false;
47738            }
47739         });
47740         return dirty;
47741         
47742     },
47743     /**
47744      * Resets all hasChanged to 'false' -
47745      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47746      * So hasChanged storage is only to be used for this purpose
47747      * @return Boolean
47748      */
47749     resetHasChanged : function()
47750     {
47751         this.items.each(function(f){
47752            f.resetHasChanged();
47753         });
47754         
47755     },
47756     
47757     
47758     /**
47759      * Performs a predefined action (submit or load) or custom actions you define on this form.
47760      * @param {String} actionName The name of the action type
47761      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47762      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47763      * accept other config options):
47764      * <pre>
47765 Property          Type             Description
47766 ----------------  ---------------  ----------------------------------------------------------------------------------
47767 url               String           The url for the action (defaults to the form's url)
47768 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47769 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47770 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47771                                    validate the form on the client (defaults to false)
47772      * </pre>
47773      * @return {BasicForm} this
47774      */
47775     doAction : function(action, options){
47776         if(typeof action == 'string'){
47777             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47778         }
47779         if(this.fireEvent('beforeaction', this, action) !== false){
47780             this.beforeAction(action);
47781             action.run.defer(100, action);
47782         }
47783         return this;
47784     },
47785
47786     /**
47787      * Shortcut to do a submit action.
47788      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47789      * @return {BasicForm} this
47790      */
47791     submit : function(options){
47792         this.doAction('submit', options);
47793         return this;
47794     },
47795
47796     /**
47797      * Shortcut to do a load action.
47798      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47799      * @return {BasicForm} this
47800      */
47801     load : function(options){
47802         this.doAction('load', options);
47803         return this;
47804     },
47805
47806     /**
47807      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47808      * @param {Record} record The record to edit
47809      * @return {BasicForm} this
47810      */
47811     updateRecord : function(record){
47812         record.beginEdit();
47813         var fs = record.fields;
47814         fs.each(function(f){
47815             var field = this.findField(f.name);
47816             if(field){
47817                 record.set(f.name, field.getValue());
47818             }
47819         }, this);
47820         record.endEdit();
47821         return this;
47822     },
47823
47824     /**
47825      * Loads an Roo.data.Record into this form.
47826      * @param {Record} record The record to load
47827      * @return {BasicForm} this
47828      */
47829     loadRecord : function(record){
47830         this.setValues(record.data);
47831         return this;
47832     },
47833
47834     // private
47835     beforeAction : function(action){
47836         var o = action.options;
47837         
47838         if(!this.disableMask) {
47839             if(this.waitMsgTarget === true){
47840                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47841             }else if(this.waitMsgTarget){
47842                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47843                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47844             }else {
47845                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47846             }
47847         }
47848         
47849          
47850     },
47851
47852     // private
47853     afterAction : function(action, success){
47854         this.activeAction = null;
47855         var o = action.options;
47856         
47857         if(!this.disableMask) {
47858             if(this.waitMsgTarget === true){
47859                 this.el.unmask();
47860             }else if(this.waitMsgTarget){
47861                 this.waitMsgTarget.unmask();
47862             }else{
47863                 Roo.MessageBox.updateProgress(1);
47864                 Roo.MessageBox.hide();
47865             }
47866         }
47867         
47868         if(success){
47869             if(o.reset){
47870                 this.reset();
47871             }
47872             Roo.callback(o.success, o.scope, [this, action]);
47873             this.fireEvent('actioncomplete', this, action);
47874             
47875         }else{
47876             
47877             // failure condition..
47878             // we have a scenario where updates need confirming.
47879             // eg. if a locking scenario exists..
47880             // we look for { errors : { needs_confirm : true }} in the response.
47881             if (
47882                 (typeof(action.result) != 'undefined')  &&
47883                 (typeof(action.result.errors) != 'undefined')  &&
47884                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47885            ){
47886                 var _t = this;
47887                 Roo.MessageBox.confirm(
47888                     "Change requires confirmation",
47889                     action.result.errorMsg,
47890                     function(r) {
47891                         if (r != 'yes') {
47892                             return;
47893                         }
47894                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47895                     }
47896                     
47897                 );
47898                 
47899                 
47900                 
47901                 return;
47902             }
47903             
47904             Roo.callback(o.failure, o.scope, [this, action]);
47905             // show an error message if no failed handler is set..
47906             if (!this.hasListener('actionfailed')) {
47907                 Roo.MessageBox.alert("Error",
47908                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47909                         action.result.errorMsg :
47910                         "Saving Failed, please check your entries or try again"
47911                 );
47912             }
47913             
47914             this.fireEvent('actionfailed', this, action);
47915         }
47916         
47917     },
47918
47919     /**
47920      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47921      * @param {String} id The value to search for
47922      * @return Field
47923      */
47924     findField : function(id){
47925         var field = this.items.get(id);
47926         if(!field){
47927             this.items.each(function(f){
47928                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47929                     field = f;
47930                     return false;
47931                 }
47932             });
47933         }
47934         return field || null;
47935     },
47936
47937     /**
47938      * Add a secondary form to this one, 
47939      * Used to provide tabbed forms. One form is primary, with hidden values 
47940      * which mirror the elements from the other forms.
47941      * 
47942      * @param {Roo.form.Form} form to add.
47943      * 
47944      */
47945     addForm : function(form)
47946     {
47947        
47948         if (this.childForms.indexOf(form) > -1) {
47949             // already added..
47950             return;
47951         }
47952         this.childForms.push(form);
47953         var n = '';
47954         Roo.each(form.allItems, function (fe) {
47955             
47956             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47957             if (this.findField(n)) { // already added..
47958                 return;
47959             }
47960             var add = new Roo.form.Hidden({
47961                 name : n
47962             });
47963             add.render(this.el);
47964             
47965             this.add( add );
47966         }, this);
47967         
47968     },
47969     /**
47970      * Mark fields in this form invalid in bulk.
47971      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47972      * @return {BasicForm} this
47973      */
47974     markInvalid : function(errors){
47975         if(errors instanceof Array){
47976             for(var i = 0, len = errors.length; i < len; i++){
47977                 var fieldError = errors[i];
47978                 var f = this.findField(fieldError.id);
47979                 if(f){
47980                     f.markInvalid(fieldError.msg);
47981                 }
47982             }
47983         }else{
47984             var field, id;
47985             for(id in errors){
47986                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47987                     field.markInvalid(errors[id]);
47988                 }
47989             }
47990         }
47991         Roo.each(this.childForms || [], function (f) {
47992             f.markInvalid(errors);
47993         });
47994         
47995         return this;
47996     },
47997
47998     /**
47999      * Set values for fields in this form in bulk.
48000      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
48001      * @return {BasicForm} this
48002      */
48003     setValues : function(values){
48004         if(values instanceof Array){ // array of objects
48005             for(var i = 0, len = values.length; i < len; i++){
48006                 var v = values[i];
48007                 var f = this.findField(v.id);
48008                 if(f){
48009                     f.setValue(v.value);
48010                     if(this.trackResetOnLoad){
48011                         f.originalValue = f.getValue();
48012                     }
48013                 }
48014             }
48015         }else{ // object hash
48016             var field, id;
48017             for(id in values){
48018                 if(typeof values[id] != 'function' && (field = this.findField(id))){
48019                     
48020                     if (field.setFromData && 
48021                         field.valueField && 
48022                         field.displayField &&
48023                         // combos' with local stores can 
48024                         // be queried via setValue()
48025                         // to set their value..
48026                         (field.store && !field.store.isLocal)
48027                         ) {
48028                         // it's a combo
48029                         var sd = { };
48030                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
48031                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
48032                         field.setFromData(sd);
48033                         
48034                     } else {
48035                         field.setValue(values[id]);
48036                     }
48037                     
48038                     
48039                     if(this.trackResetOnLoad){
48040                         field.originalValue = field.getValue();
48041                     }
48042                 }
48043             }
48044         }
48045         this.resetHasChanged();
48046         
48047         
48048         Roo.each(this.childForms || [], function (f) {
48049             f.setValues(values);
48050             f.resetHasChanged();
48051         });
48052                 
48053         return this;
48054     },
48055  
48056     /**
48057      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
48058      * they are returned as an array.
48059      * @param {Boolean} asString
48060      * @return {Object}
48061      */
48062     getValues : function(asString){
48063         if (this.childForms) {
48064             // copy values from the child forms
48065             Roo.each(this.childForms, function (f) {
48066                 this.setValues(f.getValues());
48067             }, this);
48068         }
48069         
48070         // use formdata
48071         if (typeof(FormData) != 'undefined' && asString !== true) {
48072             // this relies on a 'recent' version of chrome apparently...
48073             try {
48074                 var fd = (new FormData(this.el.dom)).entries();
48075                 var ret = {};
48076                 var ent = fd.next();
48077                 while (!ent.done) {
48078                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
48079                     ent = fd.next();
48080                 };
48081                 return ret;
48082             } catch(e) {
48083                 
48084             }
48085             
48086         }
48087         
48088         
48089         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
48090         if(asString === true){
48091             return fs;
48092         }
48093         return Roo.urlDecode(fs);
48094     },
48095     
48096     /**
48097      * Returns the fields in this form as an object with key/value pairs. 
48098      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
48099      * @return {Object}
48100      */
48101     getFieldValues : function(with_hidden)
48102     {
48103         if (this.childForms) {
48104             // copy values from the child forms
48105             // should this call getFieldValues - probably not as we do not currently copy
48106             // hidden fields when we generate..
48107             Roo.each(this.childForms, function (f) {
48108                 this.setValues(f.getValues());
48109             }, this);
48110         }
48111         
48112         var ret = {};
48113         this.items.each(function(f){
48114             if (!f.getName()) {
48115                 return;
48116             }
48117             var v = f.getValue();
48118             if (f.inputType =='radio') {
48119                 if (typeof(ret[f.getName()]) == 'undefined') {
48120                     ret[f.getName()] = ''; // empty..
48121                 }
48122                 
48123                 if (!f.el.dom.checked) {
48124                     return;
48125                     
48126                 }
48127                 v = f.el.dom.value;
48128                 
48129             }
48130             
48131             // not sure if this supported any more..
48132             if ((typeof(v) == 'object') && f.getRawValue) {
48133                 v = f.getRawValue() ; // dates..
48134             }
48135             // combo boxes where name != hiddenName...
48136             if (f.name != f.getName()) {
48137                 ret[f.name] = f.getRawValue();
48138             }
48139             ret[f.getName()] = v;
48140         });
48141         
48142         return ret;
48143     },
48144
48145     /**
48146      * Clears all invalid messages in this form.
48147      * @return {BasicForm} this
48148      */
48149     clearInvalid : function(){
48150         this.items.each(function(f){
48151            f.clearInvalid();
48152         });
48153         
48154         Roo.each(this.childForms || [], function (f) {
48155             f.clearInvalid();
48156         });
48157         
48158         
48159         return this;
48160     },
48161
48162     /**
48163      * Resets this form.
48164      * @return {BasicForm} this
48165      */
48166     reset : function(){
48167         this.items.each(function(f){
48168             f.reset();
48169         });
48170         
48171         Roo.each(this.childForms || [], function (f) {
48172             f.reset();
48173         });
48174         this.resetHasChanged();
48175         
48176         return this;
48177     },
48178
48179     /**
48180      * Add Roo.form components to this form.
48181      * @param {Field} field1
48182      * @param {Field} field2 (optional)
48183      * @param {Field} etc (optional)
48184      * @return {BasicForm} this
48185      */
48186     add : function(){
48187         this.items.addAll(Array.prototype.slice.call(arguments, 0));
48188         return this;
48189     },
48190
48191
48192     /**
48193      * Removes a field from the items collection (does NOT remove its markup).
48194      * @param {Field} field
48195      * @return {BasicForm} this
48196      */
48197     remove : function(field){
48198         this.items.remove(field);
48199         return this;
48200     },
48201
48202     /**
48203      * Looks at the fields in this form, checks them for an id attribute,
48204      * and calls applyTo on the existing dom element with that id.
48205      * @return {BasicForm} this
48206      */
48207     render : function(){
48208         this.items.each(function(f){
48209             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
48210                 f.applyTo(f.id);
48211             }
48212         });
48213         return this;
48214     },
48215
48216     /**
48217      * Calls {@link Ext#apply} for all fields in this form with the passed object.
48218      * @param {Object} values
48219      * @return {BasicForm} this
48220      */
48221     applyToFields : function(o){
48222         this.items.each(function(f){
48223            Roo.apply(f, o);
48224         });
48225         return this;
48226     },
48227
48228     /**
48229      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
48230      * @param {Object} values
48231      * @return {BasicForm} this
48232      */
48233     applyIfToFields : function(o){
48234         this.items.each(function(f){
48235            Roo.applyIf(f, o);
48236         });
48237         return this;
48238     }
48239 });
48240
48241 // back compat
48242 Roo.BasicForm = Roo.form.BasicForm;
48243
48244 Roo.apply(Roo.form.BasicForm, {
48245     
48246     popover : {
48247         
48248         padding : 5,
48249         
48250         isApplied : false,
48251         
48252         isMasked : false,
48253         
48254         form : false,
48255         
48256         target : false,
48257         
48258         intervalID : false,
48259         
48260         maskEl : false,
48261         
48262         apply : function()
48263         {
48264             if(this.isApplied){
48265                 return;
48266             }
48267             
48268             this.maskEl = {
48269                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
48270                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
48271                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
48272                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
48273             };
48274             
48275             this.maskEl.top.enableDisplayMode("block");
48276             this.maskEl.left.enableDisplayMode("block");
48277             this.maskEl.bottom.enableDisplayMode("block");
48278             this.maskEl.right.enableDisplayMode("block");
48279             
48280             Roo.get(document.body).on('click', function(){
48281                 this.unmask();
48282             }, this);
48283             
48284             Roo.get(document.body).on('touchstart', function(){
48285                 this.unmask();
48286             }, this);
48287             
48288             this.isApplied = true
48289         },
48290         
48291         mask : function(form, target)
48292         {
48293             this.form = form;
48294             
48295             this.target = target;
48296             
48297             if(!this.form.errorMask || !target.el){
48298                 return;
48299             }
48300             
48301             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
48302             
48303             var ot = this.target.el.calcOffsetsTo(scrollable);
48304             
48305             var scrollTo = ot[1] - this.form.maskOffset;
48306             
48307             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
48308             
48309             scrollable.scrollTo('top', scrollTo);
48310             
48311             var el = this.target.wrap || this.target.el;
48312             
48313             var box = el.getBox();
48314             
48315             this.maskEl.top.setStyle('position', 'absolute');
48316             this.maskEl.top.setStyle('z-index', 10000);
48317             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
48318             this.maskEl.top.setLeft(0);
48319             this.maskEl.top.setTop(0);
48320             this.maskEl.top.show();
48321             
48322             this.maskEl.left.setStyle('position', 'absolute');
48323             this.maskEl.left.setStyle('z-index', 10000);
48324             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
48325             this.maskEl.left.setLeft(0);
48326             this.maskEl.left.setTop(box.y - this.padding);
48327             this.maskEl.left.show();
48328
48329             this.maskEl.bottom.setStyle('position', 'absolute');
48330             this.maskEl.bottom.setStyle('z-index', 10000);
48331             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
48332             this.maskEl.bottom.setLeft(0);
48333             this.maskEl.bottom.setTop(box.bottom + this.padding);
48334             this.maskEl.bottom.show();
48335
48336             this.maskEl.right.setStyle('position', 'absolute');
48337             this.maskEl.right.setStyle('z-index', 10000);
48338             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48339             this.maskEl.right.setLeft(box.right + this.padding);
48340             this.maskEl.right.setTop(box.y - this.padding);
48341             this.maskEl.right.show();
48342
48343             this.intervalID = window.setInterval(function() {
48344                 Roo.form.BasicForm.popover.unmask();
48345             }, 10000);
48346
48347             window.onwheel = function(){ return false;};
48348             
48349             (function(){ this.isMasked = true; }).defer(500, this);
48350             
48351         },
48352         
48353         unmask : function()
48354         {
48355             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48356                 return;
48357             }
48358             
48359             this.maskEl.top.setStyle('position', 'absolute');
48360             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48361             this.maskEl.top.hide();
48362
48363             this.maskEl.left.setStyle('position', 'absolute');
48364             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48365             this.maskEl.left.hide();
48366
48367             this.maskEl.bottom.setStyle('position', 'absolute');
48368             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48369             this.maskEl.bottom.hide();
48370
48371             this.maskEl.right.setStyle('position', 'absolute');
48372             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48373             this.maskEl.right.hide();
48374             
48375             window.onwheel = function(){ return true;};
48376             
48377             if(this.intervalID){
48378                 window.clearInterval(this.intervalID);
48379                 this.intervalID = false;
48380             }
48381             
48382             this.isMasked = false;
48383             
48384         }
48385         
48386     }
48387     
48388 });/*
48389  * Based on:
48390  * Ext JS Library 1.1.1
48391  * Copyright(c) 2006-2007, Ext JS, LLC.
48392  *
48393  * Originally Released Under LGPL - original licence link has changed is not relivant.
48394  *
48395  * Fork - LGPL
48396  * <script type="text/javascript">
48397  */
48398
48399 /**
48400  * @class Roo.form.Form
48401  * @extends Roo.form.BasicForm
48402  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48403  * @constructor
48404  * @param {Object} config Configuration options
48405  */
48406 Roo.form.Form = function(config){
48407     var xitems =  [];
48408     if (config.items) {
48409         xitems = config.items;
48410         delete config.items;
48411     }
48412    
48413     
48414     Roo.form.Form.superclass.constructor.call(this, null, config);
48415     this.url = this.url || this.action;
48416     if(!this.root){
48417         this.root = new Roo.form.Layout(Roo.applyIf({
48418             id: Roo.id()
48419         }, config));
48420     }
48421     this.active = this.root;
48422     /**
48423      * Array of all the buttons that have been added to this form via {@link addButton}
48424      * @type Array
48425      */
48426     this.buttons = [];
48427     this.allItems = [];
48428     this.addEvents({
48429         /**
48430          * @event clientvalidation
48431          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48432          * @param {Form} this
48433          * @param {Boolean} valid true if the form has passed client-side validation
48434          */
48435         clientvalidation: true,
48436         /**
48437          * @event rendered
48438          * Fires when the form is rendered
48439          * @param {Roo.form.Form} form
48440          */
48441         rendered : true
48442     });
48443     
48444     if (this.progressUrl) {
48445             // push a hidden field onto the list of fields..
48446             this.addxtype( {
48447                     xns: Roo.form, 
48448                     xtype : 'Hidden', 
48449                     name : 'UPLOAD_IDENTIFIER' 
48450             });
48451         }
48452         
48453     
48454     Roo.each(xitems, this.addxtype, this);
48455     
48456 };
48457
48458 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48459     /**
48460      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48461      */
48462     /**
48463      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48464      */
48465     /**
48466      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48467      */
48468     buttonAlign:'center',
48469
48470     /**
48471      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48472      */
48473     minButtonWidth:75,
48474
48475     /**
48476      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48477      * This property cascades to child containers if not set.
48478      */
48479     labelAlign:'left',
48480
48481     /**
48482      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48483      * fires a looping event with that state. This is required to bind buttons to the valid
48484      * state using the config value formBind:true on the button.
48485      */
48486     monitorValid : false,
48487
48488     /**
48489      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48490      */
48491     monitorPoll : 200,
48492     
48493     /**
48494      * @cfg {String} progressUrl - Url to return progress data 
48495      */
48496     
48497     progressUrl : false,
48498     /**
48499      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48500      * sending a formdata with extra parameters - eg uploaded elements.
48501      */
48502     
48503     formData : false,
48504     
48505     /**
48506      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48507      * fields are added and the column is closed. If no fields are passed the column remains open
48508      * until end() is called.
48509      * @param {Object} config The config to pass to the column
48510      * @param {Field} field1 (optional)
48511      * @param {Field} field2 (optional)
48512      * @param {Field} etc (optional)
48513      * @return Column The column container object
48514      */
48515     column : function(c){
48516         var col = new Roo.form.Column(c);
48517         this.start(col);
48518         if(arguments.length > 1){ // duplicate code required because of Opera
48519             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48520             this.end();
48521         }
48522         return col;
48523     },
48524
48525     /**
48526      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48527      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48528      * until end() is called.
48529      * @param {Object} config The config to pass to the fieldset
48530      * @param {Field} field1 (optional)
48531      * @param {Field} field2 (optional)
48532      * @param {Field} etc (optional)
48533      * @return FieldSet The fieldset container object
48534      */
48535     fieldset : function(c){
48536         var fs = new Roo.form.FieldSet(c);
48537         this.start(fs);
48538         if(arguments.length > 1){ // duplicate code required because of Opera
48539             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48540             this.end();
48541         }
48542         return fs;
48543     },
48544
48545     /**
48546      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48547      * fields are added and the container is closed. If no fields are passed the container remains open
48548      * until end() is called.
48549      * @param {Object} config The config to pass to the Layout
48550      * @param {Field} field1 (optional)
48551      * @param {Field} field2 (optional)
48552      * @param {Field} etc (optional)
48553      * @return Layout The container object
48554      */
48555     container : function(c){
48556         var l = new Roo.form.Layout(c);
48557         this.start(l);
48558         if(arguments.length > 1){ // duplicate code required because of Opera
48559             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48560             this.end();
48561         }
48562         return l;
48563     },
48564
48565     /**
48566      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48567      * @param {Object} container A Roo.form.Layout or subclass of Layout
48568      * @return {Form} this
48569      */
48570     start : function(c){
48571         // cascade label info
48572         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48573         this.active.stack.push(c);
48574         c.ownerCt = this.active;
48575         this.active = c;
48576         return this;
48577     },
48578
48579     /**
48580      * Closes the current open container
48581      * @return {Form} this
48582      */
48583     end : function(){
48584         if(this.active == this.root){
48585             return this;
48586         }
48587         this.active = this.active.ownerCt;
48588         return this;
48589     },
48590
48591     /**
48592      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48593      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48594      * as the label of the field.
48595      * @param {Field} field1
48596      * @param {Field} field2 (optional)
48597      * @param {Field} etc. (optional)
48598      * @return {Form} this
48599      */
48600     add : function(){
48601         this.active.stack.push.apply(this.active.stack, arguments);
48602         this.allItems.push.apply(this.allItems,arguments);
48603         var r = [];
48604         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48605             if(a[i].isFormField){
48606                 r.push(a[i]);
48607             }
48608         }
48609         if(r.length > 0){
48610             Roo.form.Form.superclass.add.apply(this, r);
48611         }
48612         return this;
48613     },
48614     
48615
48616     
48617     
48618     
48619      /**
48620      * Find any element that has been added to a form, using it's ID or name
48621      * This can include framesets, columns etc. along with regular fields..
48622      * @param {String} id - id or name to find.
48623      
48624      * @return {Element} e - or false if nothing found.
48625      */
48626     findbyId : function(id)
48627     {
48628         var ret = false;
48629         if (!id) {
48630             return ret;
48631         }
48632         Roo.each(this.allItems, function(f){
48633             if (f.id == id || f.name == id ){
48634                 ret = f;
48635                 return false;
48636             }
48637         });
48638         return ret;
48639     },
48640
48641     
48642     
48643     /**
48644      * Render this form into the passed container. This should only be called once!
48645      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48646      * @return {Form} this
48647      */
48648     render : function(ct)
48649     {
48650         
48651         
48652         
48653         ct = Roo.get(ct);
48654         var o = this.autoCreate || {
48655             tag: 'form',
48656             method : this.method || 'POST',
48657             id : this.id || Roo.id()
48658         };
48659         this.initEl(ct.createChild(o));
48660
48661         this.root.render(this.el);
48662         
48663        
48664              
48665         this.items.each(function(f){
48666             f.render('x-form-el-'+f.id);
48667         });
48668
48669         if(this.buttons.length > 0){
48670             // tables are required to maintain order and for correct IE layout
48671             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48672                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48673                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48674             }}, null, true);
48675             var tr = tb.getElementsByTagName('tr')[0];
48676             for(var i = 0, len = this.buttons.length; i < len; i++) {
48677                 var b = this.buttons[i];
48678                 var td = document.createElement('td');
48679                 td.className = 'x-form-btn-td';
48680                 b.render(tr.appendChild(td));
48681             }
48682         }
48683         if(this.monitorValid){ // initialize after render
48684             this.startMonitoring();
48685         }
48686         this.fireEvent('rendered', this);
48687         return this;
48688     },
48689
48690     /**
48691      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48692      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48693      * object or a valid Roo.DomHelper element config
48694      * @param {Function} handler The function called when the button is clicked
48695      * @param {Object} scope (optional) The scope of the handler function
48696      * @return {Roo.Button}
48697      */
48698     addButton : function(config, handler, scope){
48699         var bc = {
48700             handler: handler,
48701             scope: scope,
48702             minWidth: this.minButtonWidth,
48703             hideParent:true
48704         };
48705         if(typeof config == "string"){
48706             bc.text = config;
48707         }else{
48708             Roo.apply(bc, config);
48709         }
48710         var btn = new Roo.Button(null, bc);
48711         this.buttons.push(btn);
48712         return btn;
48713     },
48714
48715      /**
48716      * Adds a series of form elements (using the xtype property as the factory method.
48717      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48718      * @param {Object} config 
48719      */
48720     
48721     addxtype : function()
48722     {
48723         var ar = Array.prototype.slice.call(arguments, 0);
48724         var ret = false;
48725         for(var i = 0; i < ar.length; i++) {
48726             if (!ar[i]) {
48727                 continue; // skip -- if this happends something invalid got sent, we 
48728                 // should ignore it, as basically that interface element will not show up
48729                 // and that should be pretty obvious!!
48730             }
48731             
48732             if (Roo.form[ar[i].xtype]) {
48733                 ar[i].form = this;
48734                 var fe = Roo.factory(ar[i], Roo.form);
48735                 if (!ret) {
48736                     ret = fe;
48737                 }
48738                 fe.form = this;
48739                 if (fe.store) {
48740                     fe.store.form = this;
48741                 }
48742                 if (fe.isLayout) {  
48743                          
48744                     this.start(fe);
48745                     this.allItems.push(fe);
48746                     if (fe.items && fe.addxtype) {
48747                         fe.addxtype.apply(fe, fe.items);
48748                         delete fe.items;
48749                     }
48750                      this.end();
48751                     continue;
48752                 }
48753                 
48754                 
48755                  
48756                 this.add(fe);
48757               //  console.log('adding ' + ar[i].xtype);
48758             }
48759             if (ar[i].xtype == 'Button') {  
48760                 //console.log('adding button');
48761                 //console.log(ar[i]);
48762                 this.addButton(ar[i]);
48763                 this.allItems.push(fe);
48764                 continue;
48765             }
48766             
48767             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48768                 alert('end is not supported on xtype any more, use items');
48769             //    this.end();
48770             //    //console.log('adding end');
48771             }
48772             
48773         }
48774         return ret;
48775     },
48776     
48777     /**
48778      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48779      * option "monitorValid"
48780      */
48781     startMonitoring : function(){
48782         if(!this.bound){
48783             this.bound = true;
48784             Roo.TaskMgr.start({
48785                 run : this.bindHandler,
48786                 interval : this.monitorPoll || 200,
48787                 scope: this
48788             });
48789         }
48790     },
48791
48792     /**
48793      * Stops monitoring of the valid state of this form
48794      */
48795     stopMonitoring : function(){
48796         this.bound = false;
48797     },
48798
48799     // private
48800     bindHandler : function(){
48801         if(!this.bound){
48802             return false; // stops binding
48803         }
48804         var valid = true;
48805         this.items.each(function(f){
48806             if(!f.isValid(true)){
48807                 valid = false;
48808                 return false;
48809             }
48810         });
48811         for(var i = 0, len = this.buttons.length; i < len; i++){
48812             var btn = this.buttons[i];
48813             if(btn.formBind === true && btn.disabled === valid){
48814                 btn.setDisabled(!valid);
48815             }
48816         }
48817         this.fireEvent('clientvalidation', this, valid);
48818     }
48819     
48820     
48821     
48822     
48823     
48824     
48825     
48826     
48827 });
48828
48829
48830 // back compat
48831 Roo.Form = Roo.form.Form;
48832 /*
48833  * Based on:
48834  * Ext JS Library 1.1.1
48835  * Copyright(c) 2006-2007, Ext JS, LLC.
48836  *
48837  * Originally Released Under LGPL - original licence link has changed is not relivant.
48838  *
48839  * Fork - LGPL
48840  * <script type="text/javascript">
48841  */
48842
48843 // as we use this in bootstrap.
48844 Roo.namespace('Roo.form');
48845  /**
48846  * @class Roo.form.Action
48847  * Internal Class used to handle form actions
48848  * @constructor
48849  * @param {Roo.form.BasicForm} el The form element or its id
48850  * @param {Object} config Configuration options
48851  */
48852
48853  
48854  
48855 // define the action interface
48856 Roo.form.Action = function(form, options){
48857     this.form = form;
48858     this.options = options || {};
48859 };
48860 /**
48861  * Client Validation Failed
48862  * @const 
48863  */
48864 Roo.form.Action.CLIENT_INVALID = 'client';
48865 /**
48866  * Server Validation Failed
48867  * @const 
48868  */
48869 Roo.form.Action.SERVER_INVALID = 'server';
48870  /**
48871  * Connect to Server Failed
48872  * @const 
48873  */
48874 Roo.form.Action.CONNECT_FAILURE = 'connect';
48875 /**
48876  * Reading Data from Server Failed
48877  * @const 
48878  */
48879 Roo.form.Action.LOAD_FAILURE = 'load';
48880
48881 Roo.form.Action.prototype = {
48882     type : 'default',
48883     failureType : undefined,
48884     response : undefined,
48885     result : undefined,
48886
48887     // interface method
48888     run : function(options){
48889
48890     },
48891
48892     // interface method
48893     success : function(response){
48894
48895     },
48896
48897     // interface method
48898     handleResponse : function(response){
48899
48900     },
48901
48902     // default connection failure
48903     failure : function(response){
48904         
48905         this.response = response;
48906         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48907         this.form.afterAction(this, false);
48908     },
48909
48910     processResponse : function(response){
48911         this.response = response;
48912         if(!response.responseText){
48913             return true;
48914         }
48915         this.result = this.handleResponse(response);
48916         return this.result;
48917     },
48918
48919     // utility functions used internally
48920     getUrl : function(appendParams){
48921         var url = this.options.url || this.form.url || this.form.el.dom.action;
48922         if(appendParams){
48923             var p = this.getParams();
48924             if(p){
48925                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48926             }
48927         }
48928         return url;
48929     },
48930
48931     getMethod : function(){
48932         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
48933     },
48934
48935     getParams : function(){
48936         var bp = this.form.baseParams;
48937         var p = this.options.params;
48938         if(p){
48939             if(typeof p == "object"){
48940                 p = Roo.urlEncode(Roo.applyIf(p, bp));
48941             }else if(typeof p == 'string' && bp){
48942                 p += '&' + Roo.urlEncode(bp);
48943             }
48944         }else if(bp){
48945             p = Roo.urlEncode(bp);
48946         }
48947         return p;
48948     },
48949
48950     createCallback : function(){
48951         return {
48952             success: this.success,
48953             failure: this.failure,
48954             scope: this,
48955             timeout: (this.form.timeout*1000),
48956             upload: this.form.fileUpload ? this.success : undefined
48957         };
48958     }
48959 };
48960
48961 Roo.form.Action.Submit = function(form, options){
48962     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
48963 };
48964
48965 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
48966     type : 'submit',
48967
48968     haveProgress : false,
48969     uploadComplete : false,
48970     
48971     // uploadProgress indicator.
48972     uploadProgress : function()
48973     {
48974         if (!this.form.progressUrl) {
48975             return;
48976         }
48977         
48978         if (!this.haveProgress) {
48979             Roo.MessageBox.progress("Uploading", "Uploading");
48980         }
48981         if (this.uploadComplete) {
48982            Roo.MessageBox.hide();
48983            return;
48984         }
48985         
48986         this.haveProgress = true;
48987    
48988         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
48989         
48990         var c = new Roo.data.Connection();
48991         c.request({
48992             url : this.form.progressUrl,
48993             params: {
48994                 id : uid
48995             },
48996             method: 'GET',
48997             success : function(req){
48998                //console.log(data);
48999                 var rdata = false;
49000                 var edata;
49001                 try  {
49002                    rdata = Roo.decode(req.responseText)
49003                 } catch (e) {
49004                     Roo.log("Invalid data from server..");
49005                     Roo.log(edata);
49006                     return;
49007                 }
49008                 if (!rdata || !rdata.success) {
49009                     Roo.log(rdata);
49010                     Roo.MessageBox.alert(Roo.encode(rdata));
49011                     return;
49012                 }
49013                 var data = rdata.data;
49014                 
49015                 if (this.uploadComplete) {
49016                    Roo.MessageBox.hide();
49017                    return;
49018                 }
49019                    
49020                 if (data){
49021                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
49022                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
49023                     );
49024                 }
49025                 this.uploadProgress.defer(2000,this);
49026             },
49027        
49028             failure: function(data) {
49029                 Roo.log('progress url failed ');
49030                 Roo.log(data);
49031             },
49032             scope : this
49033         });
49034            
49035     },
49036     
49037     
49038     run : function()
49039     {
49040         // run get Values on the form, so it syncs any secondary forms.
49041         this.form.getValues();
49042         
49043         var o = this.options;
49044         var method = this.getMethod();
49045         var isPost = method == 'POST';
49046         if(o.clientValidation === false || this.form.isValid()){
49047             
49048             if (this.form.progressUrl) {
49049                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
49050                     (new Date() * 1) + '' + Math.random());
49051                     
49052             } 
49053             
49054             
49055             Roo.Ajax.request(Roo.apply(this.createCallback(), {
49056                 form:this.form.el.dom,
49057                 url:this.getUrl(!isPost),
49058                 method: method,
49059                 params:isPost ? this.getParams() : null,
49060                 isUpload: this.form.fileUpload,
49061                 formData : this.form.formData
49062             }));
49063             
49064             this.uploadProgress();
49065
49066         }else if (o.clientValidation !== false){ // client validation failed
49067             this.failureType = Roo.form.Action.CLIENT_INVALID;
49068             this.form.afterAction(this, false);
49069         }
49070     },
49071
49072     success : function(response)
49073     {
49074         this.uploadComplete= true;
49075         if (this.haveProgress) {
49076             Roo.MessageBox.hide();
49077         }
49078         
49079         
49080         var result = this.processResponse(response);
49081         if(result === true || result.success){
49082             this.form.afterAction(this, true);
49083             return;
49084         }
49085         if(result.errors){
49086             this.form.markInvalid(result.errors);
49087             this.failureType = Roo.form.Action.SERVER_INVALID;
49088         }
49089         this.form.afterAction(this, false);
49090     },
49091     failure : function(response)
49092     {
49093         this.uploadComplete= true;
49094         if (this.haveProgress) {
49095             Roo.MessageBox.hide();
49096         }
49097         
49098         this.response = response;
49099         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49100         this.form.afterAction(this, false);
49101     },
49102     
49103     handleResponse : function(response){
49104         if(this.form.errorReader){
49105             var rs = this.form.errorReader.read(response);
49106             var errors = [];
49107             if(rs.records){
49108                 for(var i = 0, len = rs.records.length; i < len; i++) {
49109                     var r = rs.records[i];
49110                     errors[i] = r.data;
49111                 }
49112             }
49113             if(errors.length < 1){
49114                 errors = null;
49115             }
49116             return {
49117                 success : rs.success,
49118                 errors : errors
49119             };
49120         }
49121         var ret = false;
49122         try {
49123             ret = Roo.decode(response.responseText);
49124         } catch (e) {
49125             ret = {
49126                 success: false,
49127                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
49128                 errors : []
49129             };
49130         }
49131         return ret;
49132         
49133     }
49134 });
49135
49136
49137 Roo.form.Action.Load = function(form, options){
49138     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
49139     this.reader = this.form.reader;
49140 };
49141
49142 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
49143     type : 'load',
49144
49145     run : function(){
49146         
49147         Roo.Ajax.request(Roo.apply(
49148                 this.createCallback(), {
49149                     method:this.getMethod(),
49150                     url:this.getUrl(false),
49151                     params:this.getParams()
49152         }));
49153     },
49154
49155     success : function(response){
49156         
49157         var result = this.processResponse(response);
49158         if(result === true || !result.success || !result.data){
49159             this.failureType = Roo.form.Action.LOAD_FAILURE;
49160             this.form.afterAction(this, false);
49161             return;
49162         }
49163         this.form.clearInvalid();
49164         this.form.setValues(result.data);
49165         this.form.afterAction(this, true);
49166     },
49167
49168     handleResponse : function(response){
49169         if(this.form.reader){
49170             var rs = this.form.reader.read(response);
49171             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
49172             return {
49173                 success : rs.success,
49174                 data : data
49175             };
49176         }
49177         return Roo.decode(response.responseText);
49178     }
49179 });
49180
49181 Roo.form.Action.ACTION_TYPES = {
49182     'load' : Roo.form.Action.Load,
49183     'submit' : Roo.form.Action.Submit
49184 };/*
49185  * Based on:
49186  * Ext JS Library 1.1.1
49187  * Copyright(c) 2006-2007, Ext JS, LLC.
49188  *
49189  * Originally Released Under LGPL - original licence link has changed is not relivant.
49190  *
49191  * Fork - LGPL
49192  * <script type="text/javascript">
49193  */
49194  
49195 /**
49196  * @class Roo.form.Layout
49197  * @extends Roo.Component
49198  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
49199  * @constructor
49200  * @param {Object} config Configuration options
49201  */
49202 Roo.form.Layout = function(config){
49203     var xitems = [];
49204     if (config.items) {
49205         xitems = config.items;
49206         delete config.items;
49207     }
49208     Roo.form.Layout.superclass.constructor.call(this, config);
49209     this.stack = [];
49210     Roo.each(xitems, this.addxtype, this);
49211      
49212 };
49213
49214 Roo.extend(Roo.form.Layout, Roo.Component, {
49215     /**
49216      * @cfg {String/Object} autoCreate
49217      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
49218      */
49219     /**
49220      * @cfg {String/Object/Function} style
49221      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
49222      * a function which returns such a specification.
49223      */
49224     /**
49225      * @cfg {String} labelAlign
49226      * Valid values are "left," "top" and "right" (defaults to "left")
49227      */
49228     /**
49229      * @cfg {Number} labelWidth
49230      * Fixed width in pixels of all field labels (defaults to undefined)
49231      */
49232     /**
49233      * @cfg {Boolean} clear
49234      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
49235      */
49236     clear : true,
49237     /**
49238      * @cfg {String} labelSeparator
49239      * The separator to use after field labels (defaults to ':')
49240      */
49241     labelSeparator : ':',
49242     /**
49243      * @cfg {Boolean} hideLabels
49244      * True to suppress the display of field labels in this layout (defaults to false)
49245      */
49246     hideLabels : false,
49247
49248     // private
49249     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
49250     
49251     isLayout : true,
49252     
49253     // private
49254     onRender : function(ct, position){
49255         if(this.el){ // from markup
49256             this.el = Roo.get(this.el);
49257         }else {  // generate
49258             var cfg = this.getAutoCreate();
49259             this.el = ct.createChild(cfg, position);
49260         }
49261         if(this.style){
49262             this.el.applyStyles(this.style);
49263         }
49264         if(this.labelAlign){
49265             this.el.addClass('x-form-label-'+this.labelAlign);
49266         }
49267         if(this.hideLabels){
49268             this.labelStyle = "display:none";
49269             this.elementStyle = "padding-left:0;";
49270         }else{
49271             if(typeof this.labelWidth == 'number'){
49272                 this.labelStyle = "width:"+this.labelWidth+"px;";
49273                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
49274             }
49275             if(this.labelAlign == 'top'){
49276                 this.labelStyle = "width:auto;";
49277                 this.elementStyle = "padding-left:0;";
49278             }
49279         }
49280         var stack = this.stack;
49281         var slen = stack.length;
49282         if(slen > 0){
49283             if(!this.fieldTpl){
49284                 var t = new Roo.Template(
49285                     '<div class="x-form-item {5}">',
49286                         '<label for="{0}" style="{2}">{1}{4}</label>',
49287                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49288                         '</div>',
49289                     '</div><div class="x-form-clear-left"></div>'
49290                 );
49291                 t.disableFormats = true;
49292                 t.compile();
49293                 Roo.form.Layout.prototype.fieldTpl = t;
49294             }
49295             for(var i = 0; i < slen; i++) {
49296                 if(stack[i].isFormField){
49297                     this.renderField(stack[i]);
49298                 }else{
49299                     this.renderComponent(stack[i]);
49300                 }
49301             }
49302         }
49303         if(this.clear){
49304             this.el.createChild({cls:'x-form-clear'});
49305         }
49306     },
49307
49308     // private
49309     renderField : function(f){
49310         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
49311                f.id, //0
49312                f.fieldLabel, //1
49313                f.labelStyle||this.labelStyle||'', //2
49314                this.elementStyle||'', //3
49315                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
49316                f.itemCls||this.itemCls||''  //5
49317        ], true).getPrevSibling());
49318     },
49319
49320     // private
49321     renderComponent : function(c){
49322         c.render(c.isLayout ? this.el : this.el.createChild());    
49323     },
49324     /**
49325      * Adds a object form elements (using the xtype property as the factory method.)
49326      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
49327      * @param {Object} config 
49328      */
49329     addxtype : function(o)
49330     {
49331         // create the lement.
49332         o.form = this.form;
49333         var fe = Roo.factory(o, Roo.form);
49334         this.form.allItems.push(fe);
49335         this.stack.push(fe);
49336         
49337         if (fe.isFormField) {
49338             this.form.items.add(fe);
49339         }
49340          
49341         return fe;
49342     }
49343 });
49344
49345 /**
49346  * @class Roo.form.Column
49347  * @extends Roo.form.Layout
49348  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49349  * @constructor
49350  * @param {Object} config Configuration options
49351  */
49352 Roo.form.Column = function(config){
49353     Roo.form.Column.superclass.constructor.call(this, config);
49354 };
49355
49356 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49357     /**
49358      * @cfg {Number/String} width
49359      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49360      */
49361     /**
49362      * @cfg {String/Object} autoCreate
49363      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49364      */
49365
49366     // private
49367     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49368
49369     // private
49370     onRender : function(ct, position){
49371         Roo.form.Column.superclass.onRender.call(this, ct, position);
49372         if(this.width){
49373             this.el.setWidth(this.width);
49374         }
49375     }
49376 });
49377
49378
49379 /**
49380  * @class Roo.form.Row
49381  * @extends Roo.form.Layout
49382  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49383  * @constructor
49384  * @param {Object} config Configuration options
49385  */
49386
49387  
49388 Roo.form.Row = function(config){
49389     Roo.form.Row.superclass.constructor.call(this, config);
49390 };
49391  
49392 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49393       /**
49394      * @cfg {Number/String} width
49395      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49396      */
49397     /**
49398      * @cfg {Number/String} height
49399      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49400      */
49401     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49402     
49403     padWidth : 20,
49404     // private
49405     onRender : function(ct, position){
49406         //console.log('row render');
49407         if(!this.rowTpl){
49408             var t = new Roo.Template(
49409                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49410                     '<label for="{0}" style="{2}">{1}{4}</label>',
49411                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49412                     '</div>',
49413                 '</div>'
49414             );
49415             t.disableFormats = true;
49416             t.compile();
49417             Roo.form.Layout.prototype.rowTpl = t;
49418         }
49419         this.fieldTpl = this.rowTpl;
49420         
49421         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49422         var labelWidth = 100;
49423         
49424         if ((this.labelAlign != 'top')) {
49425             if (typeof this.labelWidth == 'number') {
49426                 labelWidth = this.labelWidth
49427             }
49428             this.padWidth =  20 + labelWidth;
49429             
49430         }
49431         
49432         Roo.form.Column.superclass.onRender.call(this, ct, position);
49433         if(this.width){
49434             this.el.setWidth(this.width);
49435         }
49436         if(this.height){
49437             this.el.setHeight(this.height);
49438         }
49439     },
49440     
49441     // private
49442     renderField : function(f){
49443         f.fieldEl = this.fieldTpl.append(this.el, [
49444                f.id, f.fieldLabel,
49445                f.labelStyle||this.labelStyle||'',
49446                this.elementStyle||'',
49447                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49448                f.itemCls||this.itemCls||'',
49449                f.width ? f.width + this.padWidth : 160 + this.padWidth
49450        ],true);
49451     }
49452 });
49453  
49454
49455 /**
49456  * @class Roo.form.FieldSet
49457  * @extends Roo.form.Layout
49458  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49459  * @constructor
49460  * @param {Object} config Configuration options
49461  */
49462 Roo.form.FieldSet = function(config){
49463     Roo.form.FieldSet.superclass.constructor.call(this, config);
49464 };
49465
49466 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49467     /**
49468      * @cfg {String} legend
49469      * The text to display as the legend for the FieldSet (defaults to '')
49470      */
49471     /**
49472      * @cfg {String/Object} autoCreate
49473      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49474      */
49475
49476     // private
49477     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49478
49479     // private
49480     onRender : function(ct, position){
49481         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49482         if(this.legend){
49483             this.setLegend(this.legend);
49484         }
49485     },
49486
49487     // private
49488     setLegend : function(text){
49489         if(this.rendered){
49490             this.el.child('legend').update(text);
49491         }
49492     }
49493 });/*
49494  * Based on:
49495  * Ext JS Library 1.1.1
49496  * Copyright(c) 2006-2007, Ext JS, LLC.
49497  *
49498  * Originally Released Under LGPL - original licence link has changed is not relivant.
49499  *
49500  * Fork - LGPL
49501  * <script type="text/javascript">
49502  */
49503 /**
49504  * @class Roo.form.VTypes
49505  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49506  * @singleton
49507  */
49508 Roo.form.VTypes = function(){
49509     // closure these in so they are only created once.
49510     var alpha = /^[a-zA-Z_]+$/;
49511     var alphanum = /^[a-zA-Z0-9_]+$/;
49512     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49513     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49514
49515     // All these messages and functions are configurable
49516     return {
49517         /**
49518          * The function used to validate email addresses
49519          * @param {String} value The email address
49520          */
49521         'email' : function(v){
49522             return email.test(v);
49523         },
49524         /**
49525          * The error text to display when the email validation function returns false
49526          * @type String
49527          */
49528         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49529         /**
49530          * The keystroke filter mask to be applied on email input
49531          * @type RegExp
49532          */
49533         'emailMask' : /[a-z0-9_\.\-@]/i,
49534
49535         /**
49536          * The function used to validate URLs
49537          * @param {String} value The URL
49538          */
49539         'url' : function(v){
49540             return url.test(v);
49541         },
49542         /**
49543          * The error text to display when the url validation function returns false
49544          * @type String
49545          */
49546         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49547         
49548         /**
49549          * The function used to validate alpha values
49550          * @param {String} value The value
49551          */
49552         'alpha' : function(v){
49553             return alpha.test(v);
49554         },
49555         /**
49556          * The error text to display when the alpha validation function returns false
49557          * @type String
49558          */
49559         'alphaText' : 'This field should only contain letters and _',
49560         /**
49561          * The keystroke filter mask to be applied on alpha input
49562          * @type RegExp
49563          */
49564         'alphaMask' : /[a-z_]/i,
49565
49566         /**
49567          * The function used to validate alphanumeric values
49568          * @param {String} value The value
49569          */
49570         'alphanum' : function(v){
49571             return alphanum.test(v);
49572         },
49573         /**
49574          * The error text to display when the alphanumeric validation function returns false
49575          * @type String
49576          */
49577         'alphanumText' : 'This field should only contain letters, numbers and _',
49578         /**
49579          * The keystroke filter mask to be applied on alphanumeric input
49580          * @type RegExp
49581          */
49582         'alphanumMask' : /[a-z0-9_]/i
49583     };
49584 }();//<script type="text/javascript">
49585
49586 /**
49587  * @class Roo.form.FCKeditor
49588  * @extends Roo.form.TextArea
49589  * Wrapper around the FCKEditor http://www.fckeditor.net
49590  * @constructor
49591  * Creates a new FCKeditor
49592  * @param {Object} config Configuration options
49593  */
49594 Roo.form.FCKeditor = function(config){
49595     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49596     this.addEvents({
49597          /**
49598          * @event editorinit
49599          * Fired when the editor is initialized - you can add extra handlers here..
49600          * @param {FCKeditor} this
49601          * @param {Object} the FCK object.
49602          */
49603         editorinit : true
49604     });
49605     
49606     
49607 };
49608 Roo.form.FCKeditor.editors = { };
49609 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49610 {
49611     //defaultAutoCreate : {
49612     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49613     //},
49614     // private
49615     /**
49616      * @cfg {Object} fck options - see fck manual for details.
49617      */
49618     fckconfig : false,
49619     
49620     /**
49621      * @cfg {Object} fck toolbar set (Basic or Default)
49622      */
49623     toolbarSet : 'Basic',
49624     /**
49625      * @cfg {Object} fck BasePath
49626      */ 
49627     basePath : '/fckeditor/',
49628     
49629     
49630     frame : false,
49631     
49632     value : '',
49633     
49634    
49635     onRender : function(ct, position)
49636     {
49637         if(!this.el){
49638             this.defaultAutoCreate = {
49639                 tag: "textarea",
49640                 style:"width:300px;height:60px;",
49641                 autocomplete: "new-password"
49642             };
49643         }
49644         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49645         /*
49646         if(this.grow){
49647             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49648             if(this.preventScrollbars){
49649                 this.el.setStyle("overflow", "hidden");
49650             }
49651             this.el.setHeight(this.growMin);
49652         }
49653         */
49654         //console.log('onrender' + this.getId() );
49655         Roo.form.FCKeditor.editors[this.getId()] = this;
49656          
49657
49658         this.replaceTextarea() ;
49659         
49660     },
49661     
49662     getEditor : function() {
49663         return this.fckEditor;
49664     },
49665     /**
49666      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49667      * @param {Mixed} value The value to set
49668      */
49669     
49670     
49671     setValue : function(value)
49672     {
49673         //console.log('setValue: ' + value);
49674         
49675         if(typeof(value) == 'undefined') { // not sure why this is happending...
49676             return;
49677         }
49678         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49679         
49680         //if(!this.el || !this.getEditor()) {
49681         //    this.value = value;
49682             //this.setValue.defer(100,this,[value]);    
49683         //    return;
49684         //} 
49685         
49686         if(!this.getEditor()) {
49687             return;
49688         }
49689         
49690         this.getEditor().SetData(value);
49691         
49692         //
49693
49694     },
49695
49696     /**
49697      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49698      * @return {Mixed} value The field value
49699      */
49700     getValue : function()
49701     {
49702         
49703         if (this.frame && this.frame.dom.style.display == 'none') {
49704             return Roo.form.FCKeditor.superclass.getValue.call(this);
49705         }
49706         
49707         if(!this.el || !this.getEditor()) {
49708            
49709            // this.getValue.defer(100,this); 
49710             return this.value;
49711         }
49712        
49713         
49714         var value=this.getEditor().GetData();
49715         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49716         return Roo.form.FCKeditor.superclass.getValue.call(this);
49717         
49718
49719     },
49720
49721     /**
49722      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49723      * @return {Mixed} value The field value
49724      */
49725     getRawValue : function()
49726     {
49727         if (this.frame && this.frame.dom.style.display == 'none') {
49728             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49729         }
49730         
49731         if(!this.el || !this.getEditor()) {
49732             //this.getRawValue.defer(100,this); 
49733             return this.value;
49734             return;
49735         }
49736         
49737         
49738         
49739         var value=this.getEditor().GetData();
49740         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49741         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49742          
49743     },
49744     
49745     setSize : function(w,h) {
49746         
49747         
49748         
49749         //if (this.frame && this.frame.dom.style.display == 'none') {
49750         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49751         //    return;
49752         //}
49753         //if(!this.el || !this.getEditor()) {
49754         //    this.setSize.defer(100,this, [w,h]); 
49755         //    return;
49756         //}
49757         
49758         
49759         
49760         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49761         
49762         this.frame.dom.setAttribute('width', w);
49763         this.frame.dom.setAttribute('height', h);
49764         this.frame.setSize(w,h);
49765         
49766     },
49767     
49768     toggleSourceEdit : function(value) {
49769         
49770       
49771          
49772         this.el.dom.style.display = value ? '' : 'none';
49773         this.frame.dom.style.display = value ?  'none' : '';
49774         
49775     },
49776     
49777     
49778     focus: function(tag)
49779     {
49780         if (this.frame.dom.style.display == 'none') {
49781             return Roo.form.FCKeditor.superclass.focus.call(this);
49782         }
49783         if(!this.el || !this.getEditor()) {
49784             this.focus.defer(100,this, [tag]); 
49785             return;
49786         }
49787         
49788         
49789         
49790         
49791         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49792         this.getEditor().Focus();
49793         if (tgs.length) {
49794             if (!this.getEditor().Selection.GetSelection()) {
49795                 this.focus.defer(100,this, [tag]); 
49796                 return;
49797             }
49798             
49799             
49800             var r = this.getEditor().EditorDocument.createRange();
49801             r.setStart(tgs[0],0);
49802             r.setEnd(tgs[0],0);
49803             this.getEditor().Selection.GetSelection().removeAllRanges();
49804             this.getEditor().Selection.GetSelection().addRange(r);
49805             this.getEditor().Focus();
49806         }
49807         
49808     },
49809     
49810     
49811     
49812     replaceTextarea : function()
49813     {
49814         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49815             return ;
49816         }
49817         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49818         //{
49819             // We must check the elements firstly using the Id and then the name.
49820         var oTextarea = document.getElementById( this.getId() );
49821         
49822         var colElementsByName = document.getElementsByName( this.getId() ) ;
49823          
49824         oTextarea.style.display = 'none' ;
49825
49826         if ( oTextarea.tabIndex ) {            
49827             this.TabIndex = oTextarea.tabIndex ;
49828         }
49829         
49830         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49831         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49832         this.frame = Roo.get(this.getId() + '___Frame')
49833     },
49834     
49835     _getConfigHtml : function()
49836     {
49837         var sConfig = '' ;
49838
49839         for ( var o in this.fckconfig ) {
49840             sConfig += sConfig.length > 0  ? '&amp;' : '';
49841             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49842         }
49843
49844         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49845     },
49846     
49847     
49848     _getIFrameHtml : function()
49849     {
49850         var sFile = 'fckeditor.html' ;
49851         /* no idea what this is about..
49852         try
49853         {
49854             if ( (/fcksource=true/i).test( window.top.location.search ) )
49855                 sFile = 'fckeditor.original.html' ;
49856         }
49857         catch (e) { 
49858         */
49859
49860         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49861         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49862         
49863         
49864         var html = '<iframe id="' + this.getId() +
49865             '___Frame" src="' + sLink +
49866             '" width="' + this.width +
49867             '" height="' + this.height + '"' +
49868             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49869             ' frameborder="0" scrolling="no"></iframe>' ;
49870
49871         return html ;
49872     },
49873     
49874     _insertHtmlBefore : function( html, element )
49875     {
49876         if ( element.insertAdjacentHTML )       {
49877             // IE
49878             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49879         } else { // Gecko
49880             var oRange = document.createRange() ;
49881             oRange.setStartBefore( element ) ;
49882             var oFragment = oRange.createContextualFragment( html );
49883             element.parentNode.insertBefore( oFragment, element ) ;
49884         }
49885     }
49886     
49887     
49888   
49889     
49890     
49891     
49892     
49893
49894 });
49895
49896 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49897
49898 function FCKeditor_OnComplete(editorInstance){
49899     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49900     f.fckEditor = editorInstance;
49901     //console.log("loaded");
49902     f.fireEvent('editorinit', f, editorInstance);
49903
49904   
49905
49906  
49907
49908
49909
49910
49911
49912
49913
49914
49915
49916
49917
49918
49919
49920
49921
49922 //<script type="text/javascript">
49923 /**
49924  * @class Roo.form.GridField
49925  * @extends Roo.form.Field
49926  * Embed a grid (or editable grid into a form)
49927  * STATUS ALPHA
49928  * 
49929  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
49930  * it needs 
49931  * xgrid.store = Roo.data.Store
49932  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
49933  * xgrid.store.reader = Roo.data.JsonReader 
49934  * 
49935  * 
49936  * @constructor
49937  * Creates a new GridField
49938  * @param {Object} config Configuration options
49939  */
49940 Roo.form.GridField = function(config){
49941     Roo.form.GridField.superclass.constructor.call(this, config);
49942      
49943 };
49944
49945 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
49946     /**
49947      * @cfg {Number} width  - used to restrict width of grid..
49948      */
49949     width : 100,
49950     /**
49951      * @cfg {Number} height - used to restrict height of grid..
49952      */
49953     height : 50,
49954      /**
49955      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
49956          * 
49957          *}
49958      */
49959     xgrid : false, 
49960     /**
49961      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49962      * {tag: "input", type: "checkbox", autocomplete: "off"})
49963      */
49964    // defaultAutoCreate : { tag: 'div' },
49965     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
49966     /**
49967      * @cfg {String} addTitle Text to include for adding a title.
49968      */
49969     addTitle : false,
49970     //
49971     onResize : function(){
49972         Roo.form.Field.superclass.onResize.apply(this, arguments);
49973     },
49974
49975     initEvents : function(){
49976         // Roo.form.Checkbox.superclass.initEvents.call(this);
49977         // has no events...
49978        
49979     },
49980
49981
49982     getResizeEl : function(){
49983         return this.wrap;
49984     },
49985
49986     getPositionEl : function(){
49987         return this.wrap;
49988     },
49989
49990     // private
49991     onRender : function(ct, position){
49992         
49993         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
49994         var style = this.style;
49995         delete this.style;
49996         
49997         Roo.form.GridField.superclass.onRender.call(this, ct, position);
49998         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
49999         this.viewEl = this.wrap.createChild({ tag: 'div' });
50000         if (style) {
50001             this.viewEl.applyStyles(style);
50002         }
50003         if (this.width) {
50004             this.viewEl.setWidth(this.width);
50005         }
50006         if (this.height) {
50007             this.viewEl.setHeight(this.height);
50008         }
50009         //if(this.inputValue !== undefined){
50010         //this.setValue(this.value);
50011         
50012         
50013         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
50014         
50015         
50016         this.grid.render();
50017         this.grid.getDataSource().on('remove', this.refreshValue, this);
50018         this.grid.getDataSource().on('update', this.refreshValue, this);
50019         this.grid.on('afteredit', this.refreshValue, this);
50020  
50021     },
50022      
50023     
50024     /**
50025      * Sets the value of the item. 
50026      * @param {String} either an object  or a string..
50027      */
50028     setValue : function(v){
50029         //this.value = v;
50030         v = v || []; // empty set..
50031         // this does not seem smart - it really only affects memoryproxy grids..
50032         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
50033             var ds = this.grid.getDataSource();
50034             // assumes a json reader..
50035             var data = {}
50036             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
50037             ds.loadData( data);
50038         }
50039         // clear selection so it does not get stale.
50040         if (this.grid.sm) { 
50041             this.grid.sm.clearSelections();
50042         }
50043         
50044         Roo.form.GridField.superclass.setValue.call(this, v);
50045         this.refreshValue();
50046         // should load data in the grid really....
50047     },
50048     
50049     // private
50050     refreshValue: function() {
50051          var val = [];
50052         this.grid.getDataSource().each(function(r) {
50053             val.push(r.data);
50054         });
50055         this.el.dom.value = Roo.encode(val);
50056     }
50057     
50058      
50059     
50060     
50061 });/*
50062  * Based on:
50063  * Ext JS Library 1.1.1
50064  * Copyright(c) 2006-2007, Ext JS, LLC.
50065  *
50066  * Originally Released Under LGPL - original licence link has changed is not relivant.
50067  *
50068  * Fork - LGPL
50069  * <script type="text/javascript">
50070  */
50071 /**
50072  * @class Roo.form.DisplayField
50073  * @extends Roo.form.Field
50074  * A generic Field to display non-editable data.
50075  * @cfg {Boolean} closable (true|false) default false
50076  * @constructor
50077  * Creates a new Display Field item.
50078  * @param {Object} config Configuration options
50079  */
50080 Roo.form.DisplayField = function(config){
50081     Roo.form.DisplayField.superclass.constructor.call(this, config);
50082     
50083     this.addEvents({
50084         /**
50085          * @event close
50086          * Fires after the click the close btn
50087              * @param {Roo.form.DisplayField} this
50088              */
50089         close : true
50090     });
50091 };
50092
50093 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
50094     inputType:      'hidden',
50095     allowBlank:     true,
50096     readOnly:         true,
50097     
50098  
50099     /**
50100      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50101      */
50102     focusClass : undefined,
50103     /**
50104      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50105      */
50106     fieldClass: 'x-form-field',
50107     
50108      /**
50109      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
50110      */
50111     valueRenderer: undefined,
50112     
50113     width: 100,
50114     /**
50115      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50116      * {tag: "input", type: "checkbox", autocomplete: "off"})
50117      */
50118      
50119  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
50120  
50121     closable : false,
50122     
50123     onResize : function(){
50124         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
50125         
50126     },
50127
50128     initEvents : function(){
50129         // Roo.form.Checkbox.superclass.initEvents.call(this);
50130         // has no events...
50131         
50132         if(this.closable){
50133             this.closeEl.on('click', this.onClose, this);
50134         }
50135        
50136     },
50137
50138
50139     getResizeEl : function(){
50140         return this.wrap;
50141     },
50142
50143     getPositionEl : function(){
50144         return this.wrap;
50145     },
50146
50147     // private
50148     onRender : function(ct, position){
50149         
50150         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
50151         //if(this.inputValue !== undefined){
50152         this.wrap = this.el.wrap();
50153         
50154         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
50155         
50156         if(this.closable){
50157             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
50158         }
50159         
50160         if (this.bodyStyle) {
50161             this.viewEl.applyStyles(this.bodyStyle);
50162         }
50163         //this.viewEl.setStyle('padding', '2px');
50164         
50165         this.setValue(this.value);
50166         
50167     },
50168 /*
50169     // private
50170     initValue : Roo.emptyFn,
50171
50172   */
50173
50174         // private
50175     onClick : function(){
50176         
50177     },
50178
50179     /**
50180      * Sets the checked state of the checkbox.
50181      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
50182      */
50183     setValue : function(v){
50184         this.value = v;
50185         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
50186         // this might be called before we have a dom element..
50187         if (!this.viewEl) {
50188             return;
50189         }
50190         this.viewEl.dom.innerHTML = html;
50191         Roo.form.DisplayField.superclass.setValue.call(this, v);
50192
50193     },
50194     
50195     onClose : function(e)
50196     {
50197         e.preventDefault();
50198         
50199         this.fireEvent('close', this);
50200     }
50201 });/*
50202  * 
50203  * Licence- LGPL
50204  * 
50205  */
50206
50207 /**
50208  * @class Roo.form.DayPicker
50209  * @extends Roo.form.Field
50210  * A Day picker show [M] [T] [W] ....
50211  * @constructor
50212  * Creates a new Day Picker
50213  * @param {Object} config Configuration options
50214  */
50215 Roo.form.DayPicker= function(config){
50216     Roo.form.DayPicker.superclass.constructor.call(this, config);
50217      
50218 };
50219
50220 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
50221     /**
50222      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50223      */
50224     focusClass : undefined,
50225     /**
50226      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50227      */
50228     fieldClass: "x-form-field",
50229    
50230     /**
50231      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50232      * {tag: "input", type: "checkbox", autocomplete: "off"})
50233      */
50234     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
50235     
50236    
50237     actionMode : 'viewEl', 
50238     //
50239     // private
50240  
50241     inputType : 'hidden',
50242     
50243      
50244     inputElement: false, // real input element?
50245     basedOn: false, // ????
50246     
50247     isFormField: true, // not sure where this is needed!!!!
50248
50249     onResize : function(){
50250         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
50251         if(!this.boxLabel){
50252             this.el.alignTo(this.wrap, 'c-c');
50253         }
50254     },
50255
50256     initEvents : function(){
50257         Roo.form.Checkbox.superclass.initEvents.call(this);
50258         this.el.on("click", this.onClick,  this);
50259         this.el.on("change", this.onClick,  this);
50260     },
50261
50262
50263     getResizeEl : function(){
50264         return this.wrap;
50265     },
50266
50267     getPositionEl : function(){
50268         return this.wrap;
50269     },
50270
50271     
50272     // private
50273     onRender : function(ct, position){
50274         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
50275        
50276         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
50277         
50278         var r1 = '<table><tr>';
50279         var r2 = '<tr class="x-form-daypick-icons">';
50280         for (var i=0; i < 7; i++) {
50281             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
50282             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
50283         }
50284         
50285         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
50286         viewEl.select('img').on('click', this.onClick, this);
50287         this.viewEl = viewEl;   
50288         
50289         
50290         // this will not work on Chrome!!!
50291         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
50292         this.el.on('propertychange', this.setFromHidden,  this);  //ie
50293         
50294         
50295           
50296
50297     },
50298
50299     // private
50300     initValue : Roo.emptyFn,
50301
50302     /**
50303      * Returns the checked state of the checkbox.
50304      * @return {Boolean} True if checked, else false
50305      */
50306     getValue : function(){
50307         return this.el.dom.value;
50308         
50309     },
50310
50311         // private
50312     onClick : function(e){ 
50313         //this.setChecked(!this.checked);
50314         Roo.get(e.target).toggleClass('x-menu-item-checked');
50315         this.refreshValue();
50316         //if(this.el.dom.checked != this.checked){
50317         //    this.setValue(this.el.dom.checked);
50318        // }
50319     },
50320     
50321     // private
50322     refreshValue : function()
50323     {
50324         var val = '';
50325         this.viewEl.select('img',true).each(function(e,i,n)  {
50326             val += e.is(".x-menu-item-checked") ? String(n) : '';
50327         });
50328         this.setValue(val, true);
50329     },
50330
50331     /**
50332      * Sets the checked state of the checkbox.
50333      * On is always based on a string comparison between inputValue and the param.
50334      * @param {Boolean/String} value - the value to set 
50335      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
50336      */
50337     setValue : function(v,suppressEvent){
50338         if (!this.el.dom) {
50339             return;
50340         }
50341         var old = this.el.dom.value ;
50342         this.el.dom.value = v;
50343         if (suppressEvent) {
50344             return ;
50345         }
50346          
50347         // update display..
50348         this.viewEl.select('img',true).each(function(e,i,n)  {
50349             
50350             var on = e.is(".x-menu-item-checked");
50351             var newv = v.indexOf(String(n)) > -1;
50352             if (on != newv) {
50353                 e.toggleClass('x-menu-item-checked');
50354             }
50355             
50356         });
50357         
50358         
50359         this.fireEvent('change', this, v, old);
50360         
50361         
50362     },
50363    
50364     // handle setting of hidden value by some other method!!?!?
50365     setFromHidden: function()
50366     {
50367         if(!this.el){
50368             return;
50369         }
50370         //console.log("SET FROM HIDDEN");
50371         //alert('setFrom hidden');
50372         this.setValue(this.el.dom.value);
50373     },
50374     
50375     onDestroy : function()
50376     {
50377         if(this.viewEl){
50378             Roo.get(this.viewEl).remove();
50379         }
50380          
50381         Roo.form.DayPicker.superclass.onDestroy.call(this);
50382     }
50383
50384 });/*
50385  * RooJS Library 1.1.1
50386  * Copyright(c) 2008-2011  Alan Knowles
50387  *
50388  * License - LGPL
50389  */
50390  
50391
50392 /**
50393  * @class Roo.form.ComboCheck
50394  * @extends Roo.form.ComboBox
50395  * A combobox for multiple select items.
50396  *
50397  * FIXME - could do with a reset button..
50398  * 
50399  * @constructor
50400  * Create a new ComboCheck
50401  * @param {Object} config Configuration options
50402  */
50403 Roo.form.ComboCheck = function(config){
50404     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50405     // should verify some data...
50406     // like
50407     // hiddenName = required..
50408     // displayField = required
50409     // valudField == required
50410     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50411     var _t = this;
50412     Roo.each(req, function(e) {
50413         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50414             throw "Roo.form.ComboCheck : missing value for: " + e;
50415         }
50416     });
50417     
50418     
50419 };
50420
50421 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50422      
50423      
50424     editable : false,
50425      
50426     selectedClass: 'x-menu-item-checked', 
50427     
50428     // private
50429     onRender : function(ct, position){
50430         var _t = this;
50431         
50432         
50433         
50434         if(!this.tpl){
50435             var cls = 'x-combo-list';
50436
50437             
50438             this.tpl =  new Roo.Template({
50439                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50440                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50441                    '<span>{' + this.displayField + '}</span>' +
50442                     '</div>' 
50443                 
50444             });
50445         }
50446  
50447         
50448         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50449         this.view.singleSelect = false;
50450         this.view.multiSelect = true;
50451         this.view.toggleSelect = true;
50452         this.pageTb.add(new Roo.Toolbar.Fill(), {
50453             
50454             text: 'Done',
50455             handler: function()
50456             {
50457                 _t.collapse();
50458             }
50459         });
50460     },
50461     
50462     onViewOver : function(e, t){
50463         // do nothing...
50464         return;
50465         
50466     },
50467     
50468     onViewClick : function(doFocus,index){
50469         return;
50470         
50471     },
50472     select: function () {
50473         //Roo.log("SELECT CALLED");
50474     },
50475      
50476     selectByValue : function(xv, scrollIntoView){
50477         var ar = this.getValueArray();
50478         var sels = [];
50479         
50480         Roo.each(ar, function(v) {
50481             if(v === undefined || v === null){
50482                 return;
50483             }
50484             var r = this.findRecord(this.valueField, v);
50485             if(r){
50486                 sels.push(this.store.indexOf(r))
50487                 
50488             }
50489         },this);
50490         this.view.select(sels);
50491         return false;
50492     },
50493     
50494     
50495     
50496     onSelect : function(record, index){
50497        // Roo.log("onselect Called");
50498        // this is only called by the clear button now..
50499         this.view.clearSelections();
50500         this.setValue('[]');
50501         if (this.value != this.valueBefore) {
50502             this.fireEvent('change', this, this.value, this.valueBefore);
50503             this.valueBefore = this.value;
50504         }
50505     },
50506     getValueArray : function()
50507     {
50508         var ar = [] ;
50509         
50510         try {
50511             //Roo.log(this.value);
50512             if (typeof(this.value) == 'undefined') {
50513                 return [];
50514             }
50515             var ar = Roo.decode(this.value);
50516             return  ar instanceof Array ? ar : []; //?? valid?
50517             
50518         } catch(e) {
50519             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50520             return [];
50521         }
50522          
50523     },
50524     expand : function ()
50525     {
50526         
50527         Roo.form.ComboCheck.superclass.expand.call(this);
50528         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50529         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50530         
50531
50532     },
50533     
50534     collapse : function(){
50535         Roo.form.ComboCheck.superclass.collapse.call(this);
50536         var sl = this.view.getSelectedIndexes();
50537         var st = this.store;
50538         var nv = [];
50539         var tv = [];
50540         var r;
50541         Roo.each(sl, function(i) {
50542             r = st.getAt(i);
50543             nv.push(r.get(this.valueField));
50544         },this);
50545         this.setValue(Roo.encode(nv));
50546         if (this.value != this.valueBefore) {
50547
50548             this.fireEvent('change', this, this.value, this.valueBefore);
50549             this.valueBefore = this.value;
50550         }
50551         
50552     },
50553     
50554     setValue : function(v){
50555         // Roo.log(v);
50556         this.value = v;
50557         
50558         var vals = this.getValueArray();
50559         var tv = [];
50560         Roo.each(vals, function(k) {
50561             var r = this.findRecord(this.valueField, k);
50562             if(r){
50563                 tv.push(r.data[this.displayField]);
50564             }else if(this.valueNotFoundText !== undefined){
50565                 tv.push( this.valueNotFoundText );
50566             }
50567         },this);
50568        // Roo.log(tv);
50569         
50570         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50571         this.hiddenField.value = v;
50572         this.value = v;
50573     }
50574     
50575 });/*
50576  * Based on:
50577  * Ext JS Library 1.1.1
50578  * Copyright(c) 2006-2007, Ext JS, LLC.
50579  *
50580  * Originally Released Under LGPL - original licence link has changed is not relivant.
50581  *
50582  * Fork - LGPL
50583  * <script type="text/javascript">
50584  */
50585  
50586 /**
50587  * @class Roo.form.Signature
50588  * @extends Roo.form.Field
50589  * Signature field.  
50590  * @constructor
50591  * 
50592  * @param {Object} config Configuration options
50593  */
50594
50595 Roo.form.Signature = function(config){
50596     Roo.form.Signature.superclass.constructor.call(this, config);
50597     
50598     this.addEvents({// not in used??
50599          /**
50600          * @event confirm
50601          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50602              * @param {Roo.form.Signature} combo This combo box
50603              */
50604         'confirm' : true,
50605         /**
50606          * @event reset
50607          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50608              * @param {Roo.form.ComboBox} combo This combo box
50609              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50610              */
50611         'reset' : true
50612     });
50613 };
50614
50615 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50616     /**
50617      * @cfg {Object} labels Label to use when rendering a form.
50618      * defaults to 
50619      * labels : { 
50620      *      clear : "Clear",
50621      *      confirm : "Confirm"
50622      *  }
50623      */
50624     labels : { 
50625         clear : "Clear",
50626         confirm : "Confirm"
50627     },
50628     /**
50629      * @cfg {Number} width The signature panel width (defaults to 300)
50630      */
50631     width: 300,
50632     /**
50633      * @cfg {Number} height The signature panel height (defaults to 100)
50634      */
50635     height : 100,
50636     /**
50637      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50638      */
50639     allowBlank : false,
50640     
50641     //private
50642     // {Object} signPanel The signature SVG panel element (defaults to {})
50643     signPanel : {},
50644     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50645     isMouseDown : false,
50646     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50647     isConfirmed : false,
50648     // {String} signatureTmp SVG mapping string (defaults to empty string)
50649     signatureTmp : '',
50650     
50651     
50652     defaultAutoCreate : { // modified by initCompnoent..
50653         tag: "input",
50654         type:"hidden"
50655     },
50656
50657     // private
50658     onRender : function(ct, position){
50659         
50660         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50661         
50662         this.wrap = this.el.wrap({
50663             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50664         });
50665         
50666         this.createToolbar(this);
50667         this.signPanel = this.wrap.createChild({
50668                 tag: 'div',
50669                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50670             }, this.el
50671         );
50672             
50673         this.svgID = Roo.id();
50674         this.svgEl = this.signPanel.createChild({
50675               xmlns : 'http://www.w3.org/2000/svg',
50676               tag : 'svg',
50677               id : this.svgID + "-svg",
50678               width: this.width,
50679               height: this.height,
50680               viewBox: '0 0 '+this.width+' '+this.height,
50681               cn : [
50682                 {
50683                     tag: "rect",
50684                     id: this.svgID + "-svg-r",
50685                     width: this.width,
50686                     height: this.height,
50687                     fill: "#ffa"
50688                 },
50689                 {
50690                     tag: "line",
50691                     id: this.svgID + "-svg-l",
50692                     x1: "0", // start
50693                     y1: (this.height*0.8), // start set the line in 80% of height
50694                     x2: this.width, // end
50695                     y2: (this.height*0.8), // end set the line in 80% of height
50696                     'stroke': "#666",
50697                     'stroke-width': "1",
50698                     'stroke-dasharray': "3",
50699                     'shape-rendering': "crispEdges",
50700                     'pointer-events': "none"
50701                 },
50702                 {
50703                     tag: "path",
50704                     id: this.svgID + "-svg-p",
50705                     'stroke': "navy",
50706                     'stroke-width': "3",
50707                     'fill': "none",
50708                     'pointer-events': 'none'
50709                 }
50710               ]
50711         });
50712         this.createSVG();
50713         this.svgBox = this.svgEl.dom.getScreenCTM();
50714     },
50715     createSVG : function(){ 
50716         var svg = this.signPanel;
50717         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50718         var t = this;
50719
50720         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50721         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50722         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50723         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50724         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50725         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50726         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50727         
50728     },
50729     isTouchEvent : function(e){
50730         return e.type.match(/^touch/);
50731     },
50732     getCoords : function (e) {
50733         var pt    = this.svgEl.dom.createSVGPoint();
50734         pt.x = e.clientX; 
50735         pt.y = e.clientY;
50736         if (this.isTouchEvent(e)) {
50737             pt.x =  e.targetTouches[0].clientX;
50738             pt.y = e.targetTouches[0].clientY;
50739         }
50740         var a = this.svgEl.dom.getScreenCTM();
50741         var b = a.inverse();
50742         var mx = pt.matrixTransform(b);
50743         return mx.x + ',' + mx.y;
50744     },
50745     //mouse event headler 
50746     down : function (e) {
50747         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50748         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50749         
50750         this.isMouseDown = true;
50751         
50752         e.preventDefault();
50753     },
50754     move : function (e) {
50755         if (this.isMouseDown) {
50756             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50757             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50758         }
50759         
50760         e.preventDefault();
50761     },
50762     up : function (e) {
50763         this.isMouseDown = false;
50764         var sp = this.signatureTmp.split(' ');
50765         
50766         if(sp.length > 1){
50767             if(!sp[sp.length-2].match(/^L/)){
50768                 sp.pop();
50769                 sp.pop();
50770                 sp.push("");
50771                 this.signatureTmp = sp.join(" ");
50772             }
50773         }
50774         if(this.getValue() != this.signatureTmp){
50775             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50776             this.isConfirmed = false;
50777         }
50778         e.preventDefault();
50779     },
50780     
50781     /**
50782      * Protected method that will not generally be called directly. It
50783      * is called when the editor creates its toolbar. Override this method if you need to
50784      * add custom toolbar buttons.
50785      * @param {HtmlEditor} editor
50786      */
50787     createToolbar : function(editor){
50788          function btn(id, toggle, handler){
50789             var xid = fid + '-'+ id ;
50790             return {
50791                 id : xid,
50792                 cmd : id,
50793                 cls : 'x-btn-icon x-edit-'+id,
50794                 enableToggle:toggle !== false,
50795                 scope: editor, // was editor...
50796                 handler:handler||editor.relayBtnCmd,
50797                 clickEvent:'mousedown',
50798                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50799                 tabIndex:-1
50800             };
50801         }
50802         
50803         
50804         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50805         this.tb = tb;
50806         this.tb.add(
50807            {
50808                 cls : ' x-signature-btn x-signature-'+id,
50809                 scope: editor, // was editor...
50810                 handler: this.reset,
50811                 clickEvent:'mousedown',
50812                 text: this.labels.clear
50813             },
50814             {
50815                  xtype : 'Fill',
50816                  xns: Roo.Toolbar
50817             }, 
50818             {
50819                 cls : '  x-signature-btn x-signature-'+id,
50820                 scope: editor, // was editor...
50821                 handler: this.confirmHandler,
50822                 clickEvent:'mousedown',
50823                 text: this.labels.confirm
50824             }
50825         );
50826     
50827     },
50828     //public
50829     /**
50830      * when user is clicked confirm then show this image.....
50831      * 
50832      * @return {String} Image Data URI
50833      */
50834     getImageDataURI : function(){
50835         var svg = this.svgEl.dom.parentNode.innerHTML;
50836         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50837         return src; 
50838     },
50839     /**
50840      * 
50841      * @return {Boolean} this.isConfirmed
50842      */
50843     getConfirmed : function(){
50844         return this.isConfirmed;
50845     },
50846     /**
50847      * 
50848      * @return {Number} this.width
50849      */
50850     getWidth : function(){
50851         return this.width;
50852     },
50853     /**
50854      * 
50855      * @return {Number} this.height
50856      */
50857     getHeight : function(){
50858         return this.height;
50859     },
50860     // private
50861     getSignature : function(){
50862         return this.signatureTmp;
50863     },
50864     // private
50865     reset : function(){
50866         this.signatureTmp = '';
50867         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50868         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50869         this.isConfirmed = false;
50870         Roo.form.Signature.superclass.reset.call(this);
50871     },
50872     setSignature : function(s){
50873         this.signatureTmp = s;
50874         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50875         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50876         this.setValue(s);
50877         this.isConfirmed = false;
50878         Roo.form.Signature.superclass.reset.call(this);
50879     }, 
50880     test : function(){
50881 //        Roo.log(this.signPanel.dom.contentWindow.up())
50882     },
50883     //private
50884     setConfirmed : function(){
50885         
50886         
50887         
50888 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50889     },
50890     // private
50891     confirmHandler : function(){
50892         if(!this.getSignature()){
50893             return;
50894         }
50895         
50896         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50897         this.setValue(this.getSignature());
50898         this.isConfirmed = true;
50899         
50900         this.fireEvent('confirm', this);
50901     },
50902     // private
50903     // Subclasses should provide the validation implementation by overriding this
50904     validateValue : function(value){
50905         if(this.allowBlank){
50906             return true;
50907         }
50908         
50909         if(this.isConfirmed){
50910             return true;
50911         }
50912         return false;
50913     }
50914 });/*
50915  * Based on:
50916  * Ext JS Library 1.1.1
50917  * Copyright(c) 2006-2007, Ext JS, LLC.
50918  *
50919  * Originally Released Under LGPL - original licence link has changed is not relivant.
50920  *
50921  * Fork - LGPL
50922  * <script type="text/javascript">
50923  */
50924  
50925
50926 /**
50927  * @class Roo.form.ComboBox
50928  * @extends Roo.form.TriggerField
50929  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
50930  * @constructor
50931  * Create a new ComboBox.
50932  * @param {Object} config Configuration options
50933  */
50934 Roo.form.Select = function(config){
50935     Roo.form.Select.superclass.constructor.call(this, config);
50936      
50937 };
50938
50939 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
50940     /**
50941      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
50942      */
50943     /**
50944      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
50945      * rendering into an Roo.Editor, defaults to false)
50946      */
50947     /**
50948      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
50949      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
50950      */
50951     /**
50952      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
50953      */
50954     /**
50955      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
50956      * the dropdown list (defaults to undefined, with no header element)
50957      */
50958
50959      /**
50960      * @cfg {String/Roo.Template} tpl The template to use to render the output
50961      */
50962      
50963     // private
50964     defaultAutoCreate : {tag: "select"  },
50965     /**
50966      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
50967      */
50968     listWidth: undefined,
50969     /**
50970      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
50971      * mode = 'remote' or 'text' if mode = 'local')
50972      */
50973     displayField: undefined,
50974     /**
50975      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
50976      * mode = 'remote' or 'value' if mode = 'local'). 
50977      * Note: use of a valueField requires the user make a selection
50978      * in order for a value to be mapped.
50979      */
50980     valueField: undefined,
50981     
50982     
50983     /**
50984      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
50985      * field's data value (defaults to the underlying DOM element's name)
50986      */
50987     hiddenName: undefined,
50988     /**
50989      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
50990      */
50991     listClass: '',
50992     /**
50993      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
50994      */
50995     selectedClass: 'x-combo-selected',
50996     /**
50997      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
50998      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
50999      * which displays a downward arrow icon).
51000      */
51001     triggerClass : 'x-form-arrow-trigger',
51002     /**
51003      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
51004      */
51005     shadow:'sides',
51006     /**
51007      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
51008      * anchor positions (defaults to 'tl-bl')
51009      */
51010     listAlign: 'tl-bl?',
51011     /**
51012      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
51013      */
51014     maxHeight: 300,
51015     /**
51016      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
51017      * query specified by the allQuery config option (defaults to 'query')
51018      */
51019     triggerAction: 'query',
51020     /**
51021      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
51022      * (defaults to 4, does not apply if editable = false)
51023      */
51024     minChars : 4,
51025     /**
51026      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
51027      * delay (typeAheadDelay) if it matches a known value (defaults to false)
51028      */
51029     typeAhead: false,
51030     /**
51031      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
51032      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
51033      */
51034     queryDelay: 500,
51035     /**
51036      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
51037      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
51038      */
51039     pageSize: 0,
51040     /**
51041      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
51042      * when editable = true (defaults to false)
51043      */
51044     selectOnFocus:false,
51045     /**
51046      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
51047      */
51048     queryParam: 'query',
51049     /**
51050      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
51051      * when mode = 'remote' (defaults to 'Loading...')
51052      */
51053     loadingText: 'Loading...',
51054     /**
51055      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
51056      */
51057     resizable: false,
51058     /**
51059      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
51060      */
51061     handleHeight : 8,
51062     /**
51063      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
51064      * traditional select (defaults to true)
51065      */
51066     editable: true,
51067     /**
51068      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
51069      */
51070     allQuery: '',
51071     /**
51072      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
51073      */
51074     mode: 'remote',
51075     /**
51076      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
51077      * listWidth has a higher value)
51078      */
51079     minListWidth : 70,
51080     /**
51081      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
51082      * allow the user to set arbitrary text into the field (defaults to false)
51083      */
51084     forceSelection:false,
51085     /**
51086      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
51087      * if typeAhead = true (defaults to 250)
51088      */
51089     typeAheadDelay : 250,
51090     /**
51091      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
51092      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
51093      */
51094     valueNotFoundText : undefined,
51095     
51096     /**
51097      * @cfg {String} defaultValue The value displayed after loading the store.
51098      */
51099     defaultValue: '',
51100     
51101     /**
51102      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
51103      */
51104     blockFocus : false,
51105     
51106     /**
51107      * @cfg {Boolean} disableClear Disable showing of clear button.
51108      */
51109     disableClear : false,
51110     /**
51111      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
51112      */
51113     alwaysQuery : false,
51114     
51115     //private
51116     addicon : false,
51117     editicon: false,
51118     
51119     // element that contains real text value.. (when hidden is used..)
51120      
51121     // private
51122     onRender : function(ct, position){
51123         Roo.form.Field.prototype.onRender.call(this, ct, position);
51124         
51125         if(this.store){
51126             this.store.on('beforeload', this.onBeforeLoad, this);
51127             this.store.on('load', this.onLoad, this);
51128             this.store.on('loadexception', this.onLoadException, this);
51129             this.store.load({});
51130         }
51131         
51132         
51133         
51134     },
51135
51136     // private
51137     initEvents : function(){
51138         //Roo.form.ComboBox.superclass.initEvents.call(this);
51139  
51140     },
51141
51142     onDestroy : function(){
51143        
51144         if(this.store){
51145             this.store.un('beforeload', this.onBeforeLoad, this);
51146             this.store.un('load', this.onLoad, this);
51147             this.store.un('loadexception', this.onLoadException, this);
51148         }
51149         //Roo.form.ComboBox.superclass.onDestroy.call(this);
51150     },
51151
51152     // private
51153     fireKey : function(e){
51154         if(e.isNavKeyPress() && !this.list.isVisible()){
51155             this.fireEvent("specialkey", this, e);
51156         }
51157     },
51158
51159     // private
51160     onResize: function(w, h){
51161         
51162         return; 
51163     
51164         
51165     },
51166
51167     /**
51168      * Allow or prevent the user from directly editing the field text.  If false is passed,
51169      * the user will only be able to select from the items defined in the dropdown list.  This method
51170      * is the runtime equivalent of setting the 'editable' config option at config time.
51171      * @param {Boolean} value True to allow the user to directly edit the field text
51172      */
51173     setEditable : function(value){
51174          
51175     },
51176
51177     // private
51178     onBeforeLoad : function(){
51179         
51180         Roo.log("Select before load");
51181         return;
51182     
51183         this.innerList.update(this.loadingText ?
51184                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
51185         //this.restrictHeight();
51186         this.selectedIndex = -1;
51187     },
51188
51189     // private
51190     onLoad : function(){
51191
51192     
51193         var dom = this.el.dom;
51194         dom.innerHTML = '';
51195          var od = dom.ownerDocument;
51196          
51197         if (this.emptyText) {
51198             var op = od.createElement('option');
51199             op.setAttribute('value', '');
51200             op.innerHTML = String.format('{0}', this.emptyText);
51201             dom.appendChild(op);
51202         }
51203         if(this.store.getCount() > 0){
51204            
51205             var vf = this.valueField;
51206             var df = this.displayField;
51207             this.store.data.each(function(r) {
51208                 // which colmsn to use... testing - cdoe / title..
51209                 var op = od.createElement('option');
51210                 op.setAttribute('value', r.data[vf]);
51211                 op.innerHTML = String.format('{0}', r.data[df]);
51212                 dom.appendChild(op);
51213             });
51214             if (typeof(this.defaultValue != 'undefined')) {
51215                 this.setValue(this.defaultValue);
51216             }
51217             
51218              
51219         }else{
51220             //this.onEmptyResults();
51221         }
51222         //this.el.focus();
51223     },
51224     // private
51225     onLoadException : function()
51226     {
51227         dom.innerHTML = '';
51228             
51229         Roo.log("Select on load exception");
51230         return;
51231     
51232         this.collapse();
51233         Roo.log(this.store.reader.jsonData);
51234         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
51235             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
51236         }
51237         
51238         
51239     },
51240     // private
51241     onTypeAhead : function(){
51242          
51243     },
51244
51245     // private
51246     onSelect : function(record, index){
51247         Roo.log('on select?');
51248         return;
51249         if(this.fireEvent('beforeselect', this, record, index) !== false){
51250             this.setFromData(index > -1 ? record.data : false);
51251             this.collapse();
51252             this.fireEvent('select', this, record, index);
51253         }
51254     },
51255
51256     /**
51257      * Returns the currently selected field value or empty string if no value is set.
51258      * @return {String} value The selected value
51259      */
51260     getValue : function(){
51261         var dom = this.el.dom;
51262         this.value = dom.options[dom.selectedIndex].value;
51263         return this.value;
51264         
51265     },
51266
51267     /**
51268      * Clears any text/value currently set in the field
51269      */
51270     clearValue : function(){
51271         this.value = '';
51272         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
51273         
51274     },
51275
51276     /**
51277      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
51278      * will be displayed in the field.  If the value does not match the data value of an existing item,
51279      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
51280      * Otherwise the field will be blank (although the value will still be set).
51281      * @param {String} value The value to match
51282      */
51283     setValue : function(v){
51284         var d = this.el.dom;
51285         for (var i =0; i < d.options.length;i++) {
51286             if (v == d.options[i].value) {
51287                 d.selectedIndex = i;
51288                 this.value = v;
51289                 return;
51290             }
51291         }
51292         this.clearValue();
51293     },
51294     /**
51295      * @property {Object} the last set data for the element
51296      */
51297     
51298     lastData : false,
51299     /**
51300      * Sets the value of the field based on a object which is related to the record format for the store.
51301      * @param {Object} value the value to set as. or false on reset?
51302      */
51303     setFromData : function(o){
51304         Roo.log('setfrom data?');
51305          
51306         
51307         
51308     },
51309     // private
51310     reset : function(){
51311         this.clearValue();
51312     },
51313     // private
51314     findRecord : function(prop, value){
51315         
51316         return false;
51317     
51318         var record;
51319         if(this.store.getCount() > 0){
51320             this.store.each(function(r){
51321                 if(r.data[prop] == value){
51322                     record = r;
51323                     return false;
51324                 }
51325                 return true;
51326             });
51327         }
51328         return record;
51329     },
51330     
51331     getName: function()
51332     {
51333         // returns hidden if it's set..
51334         if (!this.rendered) {return ''};
51335         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
51336         
51337     },
51338      
51339
51340     
51341
51342     // private
51343     onEmptyResults : function(){
51344         Roo.log('empty results');
51345         //this.collapse();
51346     },
51347
51348     /**
51349      * Returns true if the dropdown list is expanded, else false.
51350      */
51351     isExpanded : function(){
51352         return false;
51353     },
51354
51355     /**
51356      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51357      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51358      * @param {String} value The data value of the item to select
51359      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51360      * selected item if it is not currently in view (defaults to true)
51361      * @return {Boolean} True if the value matched an item in the list, else false
51362      */
51363     selectByValue : function(v, scrollIntoView){
51364         Roo.log('select By Value');
51365         return false;
51366     
51367         if(v !== undefined && v !== null){
51368             var r = this.findRecord(this.valueField || this.displayField, v);
51369             if(r){
51370                 this.select(this.store.indexOf(r), scrollIntoView);
51371                 return true;
51372             }
51373         }
51374         return false;
51375     },
51376
51377     /**
51378      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51379      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51380      * @param {Number} index The zero-based index of the list item to select
51381      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51382      * selected item if it is not currently in view (defaults to true)
51383      */
51384     select : function(index, scrollIntoView){
51385         Roo.log('select ');
51386         return  ;
51387         
51388         this.selectedIndex = index;
51389         this.view.select(index);
51390         if(scrollIntoView !== false){
51391             var el = this.view.getNode(index);
51392             if(el){
51393                 this.innerList.scrollChildIntoView(el, false);
51394             }
51395         }
51396     },
51397
51398       
51399
51400     // private
51401     validateBlur : function(){
51402         
51403         return;
51404         
51405     },
51406
51407     // private
51408     initQuery : function(){
51409         this.doQuery(this.getRawValue());
51410     },
51411
51412     // private
51413     doForce : function(){
51414         if(this.el.dom.value.length > 0){
51415             this.el.dom.value =
51416                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51417              
51418         }
51419     },
51420
51421     /**
51422      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51423      * query allowing the query action to be canceled if needed.
51424      * @param {String} query The SQL query to execute
51425      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51426      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51427      * saved in the current store (defaults to false)
51428      */
51429     doQuery : function(q, forceAll){
51430         
51431         Roo.log('doQuery?');
51432         if(q === undefined || q === null){
51433             q = '';
51434         }
51435         var qe = {
51436             query: q,
51437             forceAll: forceAll,
51438             combo: this,
51439             cancel:false
51440         };
51441         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51442             return false;
51443         }
51444         q = qe.query;
51445         forceAll = qe.forceAll;
51446         if(forceAll === true || (q.length >= this.minChars)){
51447             if(this.lastQuery != q || this.alwaysQuery){
51448                 this.lastQuery = q;
51449                 if(this.mode == 'local'){
51450                     this.selectedIndex = -1;
51451                     if(forceAll){
51452                         this.store.clearFilter();
51453                     }else{
51454                         this.store.filter(this.displayField, q);
51455                     }
51456                     this.onLoad();
51457                 }else{
51458                     this.store.baseParams[this.queryParam] = q;
51459                     this.store.load({
51460                         params: this.getParams(q)
51461                     });
51462                     this.expand();
51463                 }
51464             }else{
51465                 this.selectedIndex = -1;
51466                 this.onLoad();   
51467             }
51468         }
51469     },
51470
51471     // private
51472     getParams : function(q){
51473         var p = {};
51474         //p[this.queryParam] = q;
51475         if(this.pageSize){
51476             p.start = 0;
51477             p.limit = this.pageSize;
51478         }
51479         return p;
51480     },
51481
51482     /**
51483      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51484      */
51485     collapse : function(){
51486         
51487     },
51488
51489     // private
51490     collapseIf : function(e){
51491         
51492     },
51493
51494     /**
51495      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51496      */
51497     expand : function(){
51498         
51499     } ,
51500
51501     // private
51502      
51503
51504     /** 
51505     * @cfg {Boolean} grow 
51506     * @hide 
51507     */
51508     /** 
51509     * @cfg {Number} growMin 
51510     * @hide 
51511     */
51512     /** 
51513     * @cfg {Number} growMax 
51514     * @hide 
51515     */
51516     /**
51517      * @hide
51518      * @method autoSize
51519      */
51520     
51521     setWidth : function()
51522     {
51523         
51524     },
51525     getResizeEl : function(){
51526         return this.el;
51527     }
51528 });//<script type="text/javasscript">
51529  
51530
51531 /**
51532  * @class Roo.DDView
51533  * A DnD enabled version of Roo.View.
51534  * @param {Element/String} container The Element in which to create the View.
51535  * @param {String} tpl The template string used to create the markup for each element of the View
51536  * @param {Object} config The configuration properties. These include all the config options of
51537  * {@link Roo.View} plus some specific to this class.<br>
51538  * <p>
51539  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51540  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51541  * <p>
51542  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51543 .x-view-drag-insert-above {
51544         border-top:1px dotted #3366cc;
51545 }
51546 .x-view-drag-insert-below {
51547         border-bottom:1px dotted #3366cc;
51548 }
51549 </code></pre>
51550  * 
51551  */
51552  
51553 Roo.DDView = function(container, tpl, config) {
51554     Roo.DDView.superclass.constructor.apply(this, arguments);
51555     this.getEl().setStyle("outline", "0px none");
51556     this.getEl().unselectable();
51557     if (this.dragGroup) {
51558         this.setDraggable(this.dragGroup.split(","));
51559     }
51560     if (this.dropGroup) {
51561         this.setDroppable(this.dropGroup.split(","));
51562     }
51563     if (this.deletable) {
51564         this.setDeletable();
51565     }
51566     this.isDirtyFlag = false;
51567         this.addEvents({
51568                 "drop" : true
51569         });
51570 };
51571
51572 Roo.extend(Roo.DDView, Roo.View, {
51573 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51574 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51575 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51576 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51577
51578         isFormField: true,
51579
51580         reset: Roo.emptyFn,
51581         
51582         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51583
51584         validate: function() {
51585                 return true;
51586         },
51587         
51588         destroy: function() {
51589                 this.purgeListeners();
51590                 this.getEl.removeAllListeners();
51591                 this.getEl().remove();
51592                 if (this.dragZone) {
51593                         if (this.dragZone.destroy) {
51594                                 this.dragZone.destroy();
51595                         }
51596                 }
51597                 if (this.dropZone) {
51598                         if (this.dropZone.destroy) {
51599                                 this.dropZone.destroy();
51600                         }
51601                 }
51602         },
51603
51604 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51605         getName: function() {
51606                 return this.name;
51607         },
51608
51609 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51610         setValue: function(v) {
51611                 if (!this.store) {
51612                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51613                 }
51614                 var data = {};
51615                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51616                 this.store.proxy = new Roo.data.MemoryProxy(data);
51617                 this.store.load();
51618         },
51619
51620 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51621         getValue: function() {
51622                 var result = '(';
51623                 this.store.each(function(rec) {
51624                         result += rec.id + ',';
51625                 });
51626                 return result.substr(0, result.length - 1) + ')';
51627         },
51628         
51629         getIds: function() {
51630                 var i = 0, result = new Array(this.store.getCount());
51631                 this.store.each(function(rec) {
51632                         result[i++] = rec.id;
51633                 });
51634                 return result;
51635         },
51636         
51637         isDirty: function() {
51638                 return this.isDirtyFlag;
51639         },
51640
51641 /**
51642  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51643  *      whole Element becomes the target, and this causes the drop gesture to append.
51644  */
51645     getTargetFromEvent : function(e) {
51646                 var target = e.getTarget();
51647                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51648                 target = target.parentNode;
51649                 }
51650                 if (!target) {
51651                         target = this.el.dom.lastChild || this.el.dom;
51652                 }
51653                 return target;
51654     },
51655
51656 /**
51657  *      Create the drag data which consists of an object which has the property "ddel" as
51658  *      the drag proxy element. 
51659  */
51660     getDragData : function(e) {
51661         var target = this.findItemFromChild(e.getTarget());
51662                 if(target) {
51663                         this.handleSelection(e);
51664                         var selNodes = this.getSelectedNodes();
51665             var dragData = {
51666                 source: this,
51667                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51668                 nodes: selNodes,
51669                 records: []
51670                         };
51671                         var selectedIndices = this.getSelectedIndexes();
51672                         for (var i = 0; i < selectedIndices.length; i++) {
51673                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51674                         }
51675                         if (selNodes.length == 1) {
51676                                 dragData.ddel = target.cloneNode(true); // the div element
51677                         } else {
51678                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51679                                 div.className = 'multi-proxy';
51680                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51681                                         div.appendChild(selNodes[i].cloneNode(true));
51682                                 }
51683                                 dragData.ddel = div;
51684                         }
51685             //console.log(dragData)
51686             //console.log(dragData.ddel.innerHTML)
51687                         return dragData;
51688                 }
51689         //console.log('nodragData')
51690                 return false;
51691     },
51692     
51693 /**     Specify to which ddGroup items in this DDView may be dragged. */
51694     setDraggable: function(ddGroup) {
51695         if (ddGroup instanceof Array) {
51696                 Roo.each(ddGroup, this.setDraggable, this);
51697                 return;
51698         }
51699         if (this.dragZone) {
51700                 this.dragZone.addToGroup(ddGroup);
51701         } else {
51702                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51703                                 containerScroll: true,
51704                                 ddGroup: ddGroup 
51705
51706                         });
51707 //                      Draggability implies selection. DragZone's mousedown selects the element.
51708                         if (!this.multiSelect) { this.singleSelect = true; }
51709
51710 //                      Wire the DragZone's handlers up to methods in *this*
51711                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51712                 }
51713     },
51714
51715 /**     Specify from which ddGroup this DDView accepts drops. */
51716     setDroppable: function(ddGroup) {
51717         if (ddGroup instanceof Array) {
51718                 Roo.each(ddGroup, this.setDroppable, this);
51719                 return;
51720         }
51721         if (this.dropZone) {
51722                 this.dropZone.addToGroup(ddGroup);
51723         } else {
51724                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51725                                 containerScroll: true,
51726                                 ddGroup: ddGroup
51727                         });
51728
51729 //                      Wire the DropZone's handlers up to methods in *this*
51730                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51731                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51732                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51733                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51734                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51735                 }
51736     },
51737
51738 /**     Decide whether to drop above or below a View node. */
51739     getDropPoint : function(e, n, dd){
51740         if (n == this.el.dom) { return "above"; }
51741                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51742                 var c = t + (b - t) / 2;
51743                 var y = Roo.lib.Event.getPageY(e);
51744                 if(y <= c) {
51745                         return "above";
51746                 }else{
51747                         return "below";
51748                 }
51749     },
51750
51751     onNodeEnter : function(n, dd, e, data){
51752                 return false;
51753     },
51754     
51755     onNodeOver : function(n, dd, e, data){
51756                 var pt = this.getDropPoint(e, n, dd);
51757                 // set the insert point style on the target node
51758                 var dragElClass = this.dropNotAllowed;
51759                 if (pt) {
51760                         var targetElClass;
51761                         if (pt == "above"){
51762                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51763                                 targetElClass = "x-view-drag-insert-above";
51764                         } else {
51765                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51766                                 targetElClass = "x-view-drag-insert-below";
51767                         }
51768                         if (this.lastInsertClass != targetElClass){
51769                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51770                                 this.lastInsertClass = targetElClass;
51771                         }
51772                 }
51773                 return dragElClass;
51774         },
51775
51776     onNodeOut : function(n, dd, e, data){
51777                 this.removeDropIndicators(n);
51778     },
51779
51780     onNodeDrop : function(n, dd, e, data){
51781         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51782                 return false;
51783         }
51784         var pt = this.getDropPoint(e, n, dd);
51785                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51786                 if (pt == "below") { insertAt++; }
51787                 for (var i = 0; i < data.records.length; i++) {
51788                         var r = data.records[i];
51789                         var dup = this.store.getById(r.id);
51790                         if (dup && (dd != this.dragZone)) {
51791                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51792                         } else {
51793                                 if (data.copy) {
51794                                         this.store.insert(insertAt++, r.copy());
51795                                 } else {
51796                                         data.source.isDirtyFlag = true;
51797                                         r.store.remove(r);
51798                                         this.store.insert(insertAt++, r);
51799                                 }
51800                                 this.isDirtyFlag = true;
51801                         }
51802                 }
51803                 this.dragZone.cachedTarget = null;
51804                 return true;
51805     },
51806
51807     removeDropIndicators : function(n){
51808                 if(n){
51809                         Roo.fly(n).removeClass([
51810                                 "x-view-drag-insert-above",
51811                                 "x-view-drag-insert-below"]);
51812                         this.lastInsertClass = "_noclass";
51813                 }
51814     },
51815
51816 /**
51817  *      Utility method. Add a delete option to the DDView's context menu.
51818  *      @param {String} imageUrl The URL of the "delete" icon image.
51819  */
51820         setDeletable: function(imageUrl) {
51821                 if (!this.singleSelect && !this.multiSelect) {
51822                         this.singleSelect = true;
51823                 }
51824                 var c = this.getContextMenu();
51825                 this.contextMenu.on("itemclick", function(item) {
51826                         switch (item.id) {
51827                                 case "delete":
51828                                         this.remove(this.getSelectedIndexes());
51829                                         break;
51830                         }
51831                 }, this);
51832                 this.contextMenu.add({
51833                         icon: imageUrl,
51834                         id: "delete",
51835                         text: 'Delete'
51836                 });
51837         },
51838         
51839 /**     Return the context menu for this DDView. */
51840         getContextMenu: function() {
51841                 if (!this.contextMenu) {
51842 //                      Create the View's context menu
51843                         this.contextMenu = new Roo.menu.Menu({
51844                                 id: this.id + "-contextmenu"
51845                         });
51846                         this.el.on("contextmenu", this.showContextMenu, this);
51847                 }
51848                 return this.contextMenu;
51849         },
51850         
51851         disableContextMenu: function() {
51852                 if (this.contextMenu) {
51853                         this.el.un("contextmenu", this.showContextMenu, this);
51854                 }
51855         },
51856
51857         showContextMenu: function(e, item) {
51858         item = this.findItemFromChild(e.getTarget());
51859                 if (item) {
51860                         e.stopEvent();
51861                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51862                         this.contextMenu.showAt(e.getXY());
51863             }
51864     },
51865
51866 /**
51867  *      Remove {@link Roo.data.Record}s at the specified indices.
51868  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51869  */
51870     remove: function(selectedIndices) {
51871                 selectedIndices = [].concat(selectedIndices);
51872                 for (var i = 0; i < selectedIndices.length; i++) {
51873                         var rec = this.store.getAt(selectedIndices[i]);
51874                         this.store.remove(rec);
51875                 }
51876     },
51877
51878 /**
51879  *      Double click fires the event, but also, if this is draggable, and there is only one other
51880  *      related DropZone, it transfers the selected node.
51881  */
51882     onDblClick : function(e){
51883         var item = this.findItemFromChild(e.getTarget());
51884         if(item){
51885             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51886                 return false;
51887             }
51888             if (this.dragGroup) {
51889                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51890                     while (targets.indexOf(this.dropZone) > -1) {
51891                             targets.remove(this.dropZone);
51892                                 }
51893                     if (targets.length == 1) {
51894                                         this.dragZone.cachedTarget = null;
51895                         var el = Roo.get(targets[0].getEl());
51896                         var box = el.getBox(true);
51897                         targets[0].onNodeDrop(el.dom, {
51898                                 target: el.dom,
51899                                 xy: [box.x, box.y + box.height - 1]
51900                         }, null, this.getDragData(e));
51901                     }
51902                 }
51903         }
51904     },
51905     
51906     handleSelection: function(e) {
51907                 this.dragZone.cachedTarget = null;
51908         var item = this.findItemFromChild(e.getTarget());
51909         if (!item) {
51910                 this.clearSelections(true);
51911                 return;
51912         }
51913                 if (item && (this.multiSelect || this.singleSelect)){
51914                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51915                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51916                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51917                                 this.unselect(item);
51918                         } else {
51919                                 this.select(item, this.multiSelect && e.ctrlKey);
51920                                 this.lastSelection = item;
51921                         }
51922                 }
51923     },
51924
51925     onItemClick : function(item, index, e){
51926                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
51927                         return false;
51928                 }
51929                 return true;
51930     },
51931
51932     unselect : function(nodeInfo, suppressEvent){
51933                 var node = this.getNode(nodeInfo);
51934                 if(node && this.isSelected(node)){
51935                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
51936                                 Roo.fly(node).removeClass(this.selectedClass);
51937                                 this.selections.remove(node);
51938                                 if(!suppressEvent){
51939                                         this.fireEvent("selectionchange", this, this.selections);
51940                                 }
51941                         }
51942                 }
51943     }
51944 });
51945 /*
51946  * Based on:
51947  * Ext JS Library 1.1.1
51948  * Copyright(c) 2006-2007, Ext JS, LLC.
51949  *
51950  * Originally Released Under LGPL - original licence link has changed is not relivant.
51951  *
51952  * Fork - LGPL
51953  * <script type="text/javascript">
51954  */
51955  
51956 /**
51957  * @class Roo.LayoutManager
51958  * @extends Roo.util.Observable
51959  * Base class for layout managers.
51960  */
51961 Roo.LayoutManager = function(container, config){
51962     Roo.LayoutManager.superclass.constructor.call(this);
51963     this.el = Roo.get(container);
51964     // ie scrollbar fix
51965     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
51966         document.body.scroll = "no";
51967     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
51968         this.el.position('relative');
51969     }
51970     this.id = this.el.id;
51971     this.el.addClass("x-layout-container");
51972     /** false to disable window resize monitoring @type Boolean */
51973     this.monitorWindowResize = true;
51974     this.regions = {};
51975     this.addEvents({
51976         /**
51977          * @event layout
51978          * Fires when a layout is performed. 
51979          * @param {Roo.LayoutManager} this
51980          */
51981         "layout" : true,
51982         /**
51983          * @event regionresized
51984          * Fires when the user resizes a region. 
51985          * @param {Roo.LayoutRegion} region The resized region
51986          * @param {Number} newSize The new size (width for east/west, height for north/south)
51987          */
51988         "regionresized" : true,
51989         /**
51990          * @event regioncollapsed
51991          * Fires when a region is collapsed. 
51992          * @param {Roo.LayoutRegion} region The collapsed region
51993          */
51994         "regioncollapsed" : true,
51995         /**
51996          * @event regionexpanded
51997          * Fires when a region is expanded.  
51998          * @param {Roo.LayoutRegion} region The expanded region
51999          */
52000         "regionexpanded" : true
52001     });
52002     this.updating = false;
52003     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
52004 };
52005
52006 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
52007     /**
52008      * Returns true if this layout is currently being updated
52009      * @return {Boolean}
52010      */
52011     isUpdating : function(){
52012         return this.updating; 
52013     },
52014     
52015     /**
52016      * Suspend the LayoutManager from doing auto-layouts while
52017      * making multiple add or remove calls
52018      */
52019     beginUpdate : function(){
52020         this.updating = true;    
52021     },
52022     
52023     /**
52024      * Restore auto-layouts and optionally disable the manager from performing a layout
52025      * @param {Boolean} noLayout true to disable a layout update 
52026      */
52027     endUpdate : function(noLayout){
52028         this.updating = false;
52029         if(!noLayout){
52030             this.layout();
52031         }    
52032     },
52033     
52034     layout: function(){
52035         
52036     },
52037     
52038     onRegionResized : function(region, newSize){
52039         this.fireEvent("regionresized", region, newSize);
52040         this.layout();
52041     },
52042     
52043     onRegionCollapsed : function(region){
52044         this.fireEvent("regioncollapsed", region);
52045     },
52046     
52047     onRegionExpanded : function(region){
52048         this.fireEvent("regionexpanded", region);
52049     },
52050         
52051     /**
52052      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
52053      * performs box-model adjustments.
52054      * @return {Object} The size as an object {width: (the width), height: (the height)}
52055      */
52056     getViewSize : function(){
52057         var size;
52058         if(this.el.dom != document.body){
52059             size = this.el.getSize();
52060         }else{
52061             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
52062         }
52063         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
52064         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
52065         return size;
52066     },
52067     
52068     /**
52069      * Returns the Element this layout is bound to.
52070      * @return {Roo.Element}
52071      */
52072     getEl : function(){
52073         return this.el;
52074     },
52075     
52076     /**
52077      * Returns the specified region.
52078      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
52079      * @return {Roo.LayoutRegion}
52080      */
52081     getRegion : function(target){
52082         return this.regions[target.toLowerCase()];
52083     },
52084     
52085     onWindowResize : function(){
52086         if(this.monitorWindowResize){
52087             this.layout();
52088         }
52089     }
52090 });/*
52091  * Based on:
52092  * Ext JS Library 1.1.1
52093  * Copyright(c) 2006-2007, Ext JS, LLC.
52094  *
52095  * Originally Released Under LGPL - original licence link has changed is not relivant.
52096  *
52097  * Fork - LGPL
52098  * <script type="text/javascript">
52099  */
52100 /**
52101  * @class Roo.BorderLayout
52102  * @extends Roo.LayoutManager
52103  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
52104  * please see: <br><br>
52105  * <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>
52106  * <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>
52107  * Example:
52108  <pre><code>
52109  var layout = new Roo.BorderLayout(document.body, {
52110     north: {
52111         initialSize: 25,
52112         titlebar: false
52113     },
52114     west: {
52115         split:true,
52116         initialSize: 200,
52117         minSize: 175,
52118         maxSize: 400,
52119         titlebar: true,
52120         collapsible: true
52121     },
52122     east: {
52123         split:true,
52124         initialSize: 202,
52125         minSize: 175,
52126         maxSize: 400,
52127         titlebar: true,
52128         collapsible: true
52129     },
52130     south: {
52131         split:true,
52132         initialSize: 100,
52133         minSize: 100,
52134         maxSize: 200,
52135         titlebar: true,
52136         collapsible: true
52137     },
52138     center: {
52139         titlebar: true,
52140         autoScroll:true,
52141         resizeTabs: true,
52142         minTabWidth: 50,
52143         preferredTabWidth: 150
52144     }
52145 });
52146
52147 // shorthand
52148 var CP = Roo.ContentPanel;
52149
52150 layout.beginUpdate();
52151 layout.add("north", new CP("north", "North"));
52152 layout.add("south", new CP("south", {title: "South", closable: true}));
52153 layout.add("west", new CP("west", {title: "West"}));
52154 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
52155 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
52156 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
52157 layout.getRegion("center").showPanel("center1");
52158 layout.endUpdate();
52159 </code></pre>
52160
52161 <b>The container the layout is rendered into can be either the body element or any other element.
52162 If it is not the body element, the container needs to either be an absolute positioned element,
52163 or you will need to add "position:relative" to the css of the container.  You will also need to specify
52164 the container size if it is not the body element.</b>
52165
52166 * @constructor
52167 * Create a new BorderLayout
52168 * @param {String/HTMLElement/Element} container The container this layout is bound to
52169 * @param {Object} config Configuration options
52170  */
52171 Roo.BorderLayout = function(container, config){
52172     config = config || {};
52173     Roo.BorderLayout.superclass.constructor.call(this, container, config);
52174     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
52175     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
52176         var target = this.factory.validRegions[i];
52177         if(config[target]){
52178             this.addRegion(target, config[target]);
52179         }
52180     }
52181 };
52182
52183 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
52184     /**
52185      * Creates and adds a new region if it doesn't already exist.
52186      * @param {String} target The target region key (north, south, east, west or center).
52187      * @param {Object} config The regions config object
52188      * @return {BorderLayoutRegion} The new region
52189      */
52190     addRegion : function(target, config){
52191         if(!this.regions[target]){
52192             var r = this.factory.create(target, this, config);
52193             this.bindRegion(target, r);
52194         }
52195         return this.regions[target];
52196     },
52197
52198     // private (kinda)
52199     bindRegion : function(name, r){
52200         this.regions[name] = r;
52201         r.on("visibilitychange", this.layout, this);
52202         r.on("paneladded", this.layout, this);
52203         r.on("panelremoved", this.layout, this);
52204         r.on("invalidated", this.layout, this);
52205         r.on("resized", this.onRegionResized, this);
52206         r.on("collapsed", this.onRegionCollapsed, this);
52207         r.on("expanded", this.onRegionExpanded, this);
52208     },
52209
52210     /**
52211      * Performs a layout update.
52212      */
52213     layout : function(){
52214         if(this.updating) {
52215             return;
52216         }
52217         var size = this.getViewSize();
52218         var w = size.width;
52219         var h = size.height;
52220         var centerW = w;
52221         var centerH = h;
52222         var centerY = 0;
52223         var centerX = 0;
52224         //var x = 0, y = 0;
52225
52226         var rs = this.regions;
52227         var north = rs["north"];
52228         var south = rs["south"]; 
52229         var west = rs["west"];
52230         var east = rs["east"];
52231         var center = rs["center"];
52232         //if(this.hideOnLayout){ // not supported anymore
52233             //c.el.setStyle("display", "none");
52234         //}
52235         if(north && north.isVisible()){
52236             var b = north.getBox();
52237             var m = north.getMargins();
52238             b.width = w - (m.left+m.right);
52239             b.x = m.left;
52240             b.y = m.top;
52241             centerY = b.height + b.y + m.bottom;
52242             centerH -= centerY;
52243             north.updateBox(this.safeBox(b));
52244         }
52245         if(south && south.isVisible()){
52246             var b = south.getBox();
52247             var m = south.getMargins();
52248             b.width = w - (m.left+m.right);
52249             b.x = m.left;
52250             var totalHeight = (b.height + m.top + m.bottom);
52251             b.y = h - totalHeight + m.top;
52252             centerH -= totalHeight;
52253             south.updateBox(this.safeBox(b));
52254         }
52255         if(west && west.isVisible()){
52256             var b = west.getBox();
52257             var m = west.getMargins();
52258             b.height = centerH - (m.top+m.bottom);
52259             b.x = m.left;
52260             b.y = centerY + m.top;
52261             var totalWidth = (b.width + m.left + m.right);
52262             centerX += totalWidth;
52263             centerW -= totalWidth;
52264             west.updateBox(this.safeBox(b));
52265         }
52266         if(east && east.isVisible()){
52267             var b = east.getBox();
52268             var m = east.getMargins();
52269             b.height = centerH - (m.top+m.bottom);
52270             var totalWidth = (b.width + m.left + m.right);
52271             b.x = w - totalWidth + m.left;
52272             b.y = centerY + m.top;
52273             centerW -= totalWidth;
52274             east.updateBox(this.safeBox(b));
52275         }
52276         if(center){
52277             var m = center.getMargins();
52278             var centerBox = {
52279                 x: centerX + m.left,
52280                 y: centerY + m.top,
52281                 width: centerW - (m.left+m.right),
52282                 height: centerH - (m.top+m.bottom)
52283             };
52284             //if(this.hideOnLayout){
52285                 //center.el.setStyle("display", "block");
52286             //}
52287             center.updateBox(this.safeBox(centerBox));
52288         }
52289         this.el.repaint();
52290         this.fireEvent("layout", this);
52291     },
52292
52293     // private
52294     safeBox : function(box){
52295         box.width = Math.max(0, box.width);
52296         box.height = Math.max(0, box.height);
52297         return box;
52298     },
52299
52300     /**
52301      * Adds a ContentPanel (or subclass) to this layout.
52302      * @param {String} target The target region key (north, south, east, west or center).
52303      * @param {Roo.ContentPanel} panel The panel to add
52304      * @return {Roo.ContentPanel} The added panel
52305      */
52306     add : function(target, panel){
52307          
52308         target = target.toLowerCase();
52309         return this.regions[target].add(panel);
52310     },
52311
52312     /**
52313      * Remove a ContentPanel (or subclass) to this layout.
52314      * @param {String} target The target region key (north, south, east, west or center).
52315      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
52316      * @return {Roo.ContentPanel} The removed panel
52317      */
52318     remove : function(target, panel){
52319         target = target.toLowerCase();
52320         return this.regions[target].remove(panel);
52321     },
52322
52323     /**
52324      * Searches all regions for a panel with the specified id
52325      * @param {String} panelId
52326      * @return {Roo.ContentPanel} The panel or null if it wasn't found
52327      */
52328     findPanel : function(panelId){
52329         var rs = this.regions;
52330         for(var target in rs){
52331             if(typeof rs[target] != "function"){
52332                 var p = rs[target].getPanel(panelId);
52333                 if(p){
52334                     return p;
52335                 }
52336             }
52337         }
52338         return null;
52339     },
52340
52341     /**
52342      * Searches all regions for a panel with the specified id and activates (shows) it.
52343      * @param {String/ContentPanel} panelId The panels id or the panel itself
52344      * @return {Roo.ContentPanel} The shown panel or null
52345      */
52346     showPanel : function(panelId) {
52347       var rs = this.regions;
52348       for(var target in rs){
52349          var r = rs[target];
52350          if(typeof r != "function"){
52351             if(r.hasPanel(panelId)){
52352                return r.showPanel(panelId);
52353             }
52354          }
52355       }
52356       return null;
52357    },
52358
52359    /**
52360      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52361      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52362      */
52363     restoreState : function(provider){
52364         if(!provider){
52365             provider = Roo.state.Manager;
52366         }
52367         var sm = new Roo.LayoutStateManager();
52368         sm.init(this, provider);
52369     },
52370
52371     /**
52372      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52373      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52374      * a valid ContentPanel config object.  Example:
52375      * <pre><code>
52376 // Create the main layout
52377 var layout = new Roo.BorderLayout('main-ct', {
52378     west: {
52379         split:true,
52380         minSize: 175,
52381         titlebar: true
52382     },
52383     center: {
52384         title:'Components'
52385     }
52386 }, 'main-ct');
52387
52388 // Create and add multiple ContentPanels at once via configs
52389 layout.batchAdd({
52390    west: {
52391        id: 'source-files',
52392        autoCreate:true,
52393        title:'Ext Source Files',
52394        autoScroll:true,
52395        fitToFrame:true
52396    },
52397    center : {
52398        el: cview,
52399        autoScroll:true,
52400        fitToFrame:true,
52401        toolbar: tb,
52402        resizeEl:'cbody'
52403    }
52404 });
52405 </code></pre>
52406      * @param {Object} regions An object containing ContentPanel configs by region name
52407      */
52408     batchAdd : function(regions){
52409         this.beginUpdate();
52410         for(var rname in regions){
52411             var lr = this.regions[rname];
52412             if(lr){
52413                 this.addTypedPanels(lr, regions[rname]);
52414             }
52415         }
52416         this.endUpdate();
52417     },
52418
52419     // private
52420     addTypedPanels : function(lr, ps){
52421         if(typeof ps == 'string'){
52422             lr.add(new Roo.ContentPanel(ps));
52423         }
52424         else if(ps instanceof Array){
52425             for(var i =0, len = ps.length; i < len; i++){
52426                 this.addTypedPanels(lr, ps[i]);
52427             }
52428         }
52429         else if(!ps.events){ // raw config?
52430             var el = ps.el;
52431             delete ps.el; // prevent conflict
52432             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52433         }
52434         else {  // panel object assumed!
52435             lr.add(ps);
52436         }
52437     },
52438     /**
52439      * Adds a xtype elements to the layout.
52440      * <pre><code>
52441
52442 layout.addxtype({
52443        xtype : 'ContentPanel',
52444        region: 'west',
52445        items: [ .... ]
52446    }
52447 );
52448
52449 layout.addxtype({
52450         xtype : 'NestedLayoutPanel',
52451         region: 'west',
52452         layout: {
52453            center: { },
52454            west: { }   
52455         },
52456         items : [ ... list of content panels or nested layout panels.. ]
52457    }
52458 );
52459 </code></pre>
52460      * @param {Object} cfg Xtype definition of item to add.
52461      */
52462     addxtype : function(cfg)
52463     {
52464         // basically accepts a pannel...
52465         // can accept a layout region..!?!?
52466         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52467         
52468         if (!cfg.xtype.match(/Panel$/)) {
52469             return false;
52470         }
52471         var ret = false;
52472         
52473         if (typeof(cfg.region) == 'undefined') {
52474             Roo.log("Failed to add Panel, region was not set");
52475             Roo.log(cfg);
52476             return false;
52477         }
52478         var region = cfg.region;
52479         delete cfg.region;
52480         
52481           
52482         var xitems = [];
52483         if (cfg.items) {
52484             xitems = cfg.items;
52485             delete cfg.items;
52486         }
52487         var nb = false;
52488         
52489         switch(cfg.xtype) 
52490         {
52491             case 'ContentPanel':  // ContentPanel (el, cfg)
52492             case 'ScrollPanel':  // ContentPanel (el, cfg)
52493             case 'ViewPanel': 
52494                 if(cfg.autoCreate) {
52495                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52496                 } else {
52497                     var el = this.el.createChild();
52498                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52499                 }
52500                 
52501                 this.add(region, ret);
52502                 break;
52503             
52504             
52505             case 'TreePanel': // our new panel!
52506                 cfg.el = this.el.createChild();
52507                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52508                 this.add(region, ret);
52509                 break;
52510             
52511             case 'NestedLayoutPanel': 
52512                 // create a new Layout (which is  a Border Layout...
52513                 var el = this.el.createChild();
52514                 var clayout = cfg.layout;
52515                 delete cfg.layout;
52516                 clayout.items   = clayout.items  || [];
52517                 // replace this exitems with the clayout ones..
52518                 xitems = clayout.items;
52519                  
52520                 
52521                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52522                     cfg.background = false;
52523                 }
52524                 var layout = new Roo.BorderLayout(el, clayout);
52525                 
52526                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52527                 //console.log('adding nested layout panel '  + cfg.toSource());
52528                 this.add(region, ret);
52529                 nb = {}; /// find first...
52530                 break;
52531                 
52532             case 'GridPanel': 
52533             
52534                 // needs grid and region
52535                 
52536                 //var el = this.getRegion(region).el.createChild();
52537                 var el = this.el.createChild();
52538                 // create the grid first...
52539                 
52540                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52541                 delete cfg.grid;
52542                 if (region == 'center' && this.active ) {
52543                     cfg.background = false;
52544                 }
52545                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52546                 
52547                 this.add(region, ret);
52548                 if (cfg.background) {
52549                     ret.on('activate', function(gp) {
52550                         if (!gp.grid.rendered) {
52551                             gp.grid.render();
52552                         }
52553                     });
52554                 } else {
52555                     grid.render();
52556                 }
52557                 break;
52558            
52559            
52560            
52561                 
52562                 
52563                 
52564             default:
52565                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52566                     
52567                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52568                     this.add(region, ret);
52569                 } else {
52570                 
52571                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52572                     return null;
52573                 }
52574                 
52575              // GridPanel (grid, cfg)
52576             
52577         }
52578         this.beginUpdate();
52579         // add children..
52580         var region = '';
52581         var abn = {};
52582         Roo.each(xitems, function(i)  {
52583             region = nb && i.region ? i.region : false;
52584             
52585             var add = ret.addxtype(i);
52586            
52587             if (region) {
52588                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52589                 if (!i.background) {
52590                     abn[region] = nb[region] ;
52591                 }
52592             }
52593             
52594         });
52595         this.endUpdate();
52596
52597         // make the last non-background panel active..
52598         //if (nb) { Roo.log(abn); }
52599         if (nb) {
52600             
52601             for(var r in abn) {
52602                 region = this.getRegion(r);
52603                 if (region) {
52604                     // tried using nb[r], but it does not work..
52605                      
52606                     region.showPanel(abn[r]);
52607                    
52608                 }
52609             }
52610         }
52611         return ret;
52612         
52613     }
52614 });
52615
52616 /**
52617  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52618  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52619  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52620  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52621  * <pre><code>
52622 // shorthand
52623 var CP = Roo.ContentPanel;
52624
52625 var layout = Roo.BorderLayout.create({
52626     north: {
52627         initialSize: 25,
52628         titlebar: false,
52629         panels: [new CP("north", "North")]
52630     },
52631     west: {
52632         split:true,
52633         initialSize: 200,
52634         minSize: 175,
52635         maxSize: 400,
52636         titlebar: true,
52637         collapsible: true,
52638         panels: [new CP("west", {title: "West"})]
52639     },
52640     east: {
52641         split:true,
52642         initialSize: 202,
52643         minSize: 175,
52644         maxSize: 400,
52645         titlebar: true,
52646         collapsible: true,
52647         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52648     },
52649     south: {
52650         split:true,
52651         initialSize: 100,
52652         minSize: 100,
52653         maxSize: 200,
52654         titlebar: true,
52655         collapsible: true,
52656         panels: [new CP("south", {title: "South", closable: true})]
52657     },
52658     center: {
52659         titlebar: true,
52660         autoScroll:true,
52661         resizeTabs: true,
52662         minTabWidth: 50,
52663         preferredTabWidth: 150,
52664         panels: [
52665             new CP("center1", {title: "Close Me", closable: true}),
52666             new CP("center2", {title: "Center Panel", closable: false})
52667         ]
52668     }
52669 }, document.body);
52670
52671 layout.getRegion("center").showPanel("center1");
52672 </code></pre>
52673  * @param config
52674  * @param targetEl
52675  */
52676 Roo.BorderLayout.create = function(config, targetEl){
52677     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52678     layout.beginUpdate();
52679     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52680     for(var j = 0, jlen = regions.length; j < jlen; j++){
52681         var lr = regions[j];
52682         if(layout.regions[lr] && config[lr].panels){
52683             var r = layout.regions[lr];
52684             var ps = config[lr].panels;
52685             layout.addTypedPanels(r, ps);
52686         }
52687     }
52688     layout.endUpdate();
52689     return layout;
52690 };
52691
52692 // private
52693 Roo.BorderLayout.RegionFactory = {
52694     // private
52695     validRegions : ["north","south","east","west","center"],
52696
52697     // private
52698     create : function(target, mgr, config){
52699         target = target.toLowerCase();
52700         if(config.lightweight || config.basic){
52701             return new Roo.BasicLayoutRegion(mgr, config, target);
52702         }
52703         switch(target){
52704             case "north":
52705                 return new Roo.NorthLayoutRegion(mgr, config);
52706             case "south":
52707                 return new Roo.SouthLayoutRegion(mgr, config);
52708             case "east":
52709                 return new Roo.EastLayoutRegion(mgr, config);
52710             case "west":
52711                 return new Roo.WestLayoutRegion(mgr, config);
52712             case "center":
52713                 return new Roo.CenterLayoutRegion(mgr, config);
52714         }
52715         throw 'Layout region "'+target+'" not supported.';
52716     }
52717 };/*
52718  * Based on:
52719  * Ext JS Library 1.1.1
52720  * Copyright(c) 2006-2007, Ext JS, LLC.
52721  *
52722  * Originally Released Under LGPL - original licence link has changed is not relivant.
52723  *
52724  * Fork - LGPL
52725  * <script type="text/javascript">
52726  */
52727  
52728 /**
52729  * @class Roo.BasicLayoutRegion
52730  * @extends Roo.util.Observable
52731  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52732  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52733  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52734  */
52735 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52736     this.mgr = mgr;
52737     this.position  = pos;
52738     this.events = {
52739         /**
52740          * @scope Roo.BasicLayoutRegion
52741          */
52742         
52743         /**
52744          * @event beforeremove
52745          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52746          * @param {Roo.LayoutRegion} this
52747          * @param {Roo.ContentPanel} panel The panel
52748          * @param {Object} e The cancel event object
52749          */
52750         "beforeremove" : true,
52751         /**
52752          * @event invalidated
52753          * Fires when the layout for this region is changed.
52754          * @param {Roo.LayoutRegion} this
52755          */
52756         "invalidated" : true,
52757         /**
52758          * @event visibilitychange
52759          * Fires when this region is shown or hidden 
52760          * @param {Roo.LayoutRegion} this
52761          * @param {Boolean} visibility true or false
52762          */
52763         "visibilitychange" : true,
52764         /**
52765          * @event paneladded
52766          * Fires when a panel is added. 
52767          * @param {Roo.LayoutRegion} this
52768          * @param {Roo.ContentPanel} panel The panel
52769          */
52770         "paneladded" : true,
52771         /**
52772          * @event panelremoved
52773          * Fires when a panel is removed. 
52774          * @param {Roo.LayoutRegion} this
52775          * @param {Roo.ContentPanel} panel The panel
52776          */
52777         "panelremoved" : true,
52778         /**
52779          * @event beforecollapse
52780          * Fires when this region before collapse.
52781          * @param {Roo.LayoutRegion} this
52782          */
52783         "beforecollapse" : true,
52784         /**
52785          * @event collapsed
52786          * Fires when this region is collapsed.
52787          * @param {Roo.LayoutRegion} this
52788          */
52789         "collapsed" : true,
52790         /**
52791          * @event expanded
52792          * Fires when this region is expanded.
52793          * @param {Roo.LayoutRegion} this
52794          */
52795         "expanded" : true,
52796         /**
52797          * @event slideshow
52798          * Fires when this region is slid into view.
52799          * @param {Roo.LayoutRegion} this
52800          */
52801         "slideshow" : true,
52802         /**
52803          * @event slidehide
52804          * Fires when this region slides out of view. 
52805          * @param {Roo.LayoutRegion} this
52806          */
52807         "slidehide" : true,
52808         /**
52809          * @event panelactivated
52810          * Fires when a panel is activated. 
52811          * @param {Roo.LayoutRegion} this
52812          * @param {Roo.ContentPanel} panel The activated panel
52813          */
52814         "panelactivated" : true,
52815         /**
52816          * @event resized
52817          * Fires when the user resizes this region. 
52818          * @param {Roo.LayoutRegion} this
52819          * @param {Number} newSize The new size (width for east/west, height for north/south)
52820          */
52821         "resized" : true
52822     };
52823     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52824     this.panels = new Roo.util.MixedCollection();
52825     this.panels.getKey = this.getPanelId.createDelegate(this);
52826     this.box = null;
52827     this.activePanel = null;
52828     // ensure listeners are added...
52829     
52830     if (config.listeners || config.events) {
52831         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52832             listeners : config.listeners || {},
52833             events : config.events || {}
52834         });
52835     }
52836     
52837     if(skipConfig !== true){
52838         this.applyConfig(config);
52839     }
52840 };
52841
52842 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52843     getPanelId : function(p){
52844         return p.getId();
52845     },
52846     
52847     applyConfig : function(config){
52848         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52849         this.config = config;
52850         
52851     },
52852     
52853     /**
52854      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52855      * the width, for horizontal (north, south) the height.
52856      * @param {Number} newSize The new width or height
52857      */
52858     resizeTo : function(newSize){
52859         var el = this.el ? this.el :
52860                  (this.activePanel ? this.activePanel.getEl() : null);
52861         if(el){
52862             switch(this.position){
52863                 case "east":
52864                 case "west":
52865                     el.setWidth(newSize);
52866                     this.fireEvent("resized", this, newSize);
52867                 break;
52868                 case "north":
52869                 case "south":
52870                     el.setHeight(newSize);
52871                     this.fireEvent("resized", this, newSize);
52872                 break;                
52873             }
52874         }
52875     },
52876     
52877     getBox : function(){
52878         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52879     },
52880     
52881     getMargins : function(){
52882         return this.margins;
52883     },
52884     
52885     updateBox : function(box){
52886         this.box = box;
52887         var el = this.activePanel.getEl();
52888         el.dom.style.left = box.x + "px";
52889         el.dom.style.top = box.y + "px";
52890         this.activePanel.setSize(box.width, box.height);
52891     },
52892     
52893     /**
52894      * Returns the container element for this region.
52895      * @return {Roo.Element}
52896      */
52897     getEl : function(){
52898         return this.activePanel;
52899     },
52900     
52901     /**
52902      * Returns true if this region is currently visible.
52903      * @return {Boolean}
52904      */
52905     isVisible : function(){
52906         return this.activePanel ? true : false;
52907     },
52908     
52909     setActivePanel : function(panel){
52910         panel = this.getPanel(panel);
52911         if(this.activePanel && this.activePanel != panel){
52912             this.activePanel.setActiveState(false);
52913             this.activePanel.getEl().setLeftTop(-10000,-10000);
52914         }
52915         this.activePanel = panel;
52916         panel.setActiveState(true);
52917         if(this.box){
52918             panel.setSize(this.box.width, this.box.height);
52919         }
52920         this.fireEvent("panelactivated", this, panel);
52921         this.fireEvent("invalidated");
52922     },
52923     
52924     /**
52925      * Show the specified panel.
52926      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
52927      * @return {Roo.ContentPanel} The shown panel or null
52928      */
52929     showPanel : function(panel){
52930         if(panel = this.getPanel(panel)){
52931             this.setActivePanel(panel);
52932         }
52933         return panel;
52934     },
52935     
52936     /**
52937      * Get the active panel for this region.
52938      * @return {Roo.ContentPanel} The active panel or null
52939      */
52940     getActivePanel : function(){
52941         return this.activePanel;
52942     },
52943     
52944     /**
52945      * Add the passed ContentPanel(s)
52946      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52947      * @return {Roo.ContentPanel} The panel added (if only one was added)
52948      */
52949     add : function(panel){
52950         if(arguments.length > 1){
52951             for(var i = 0, len = arguments.length; i < len; i++) {
52952                 this.add(arguments[i]);
52953             }
52954             return null;
52955         }
52956         if(this.hasPanel(panel)){
52957             this.showPanel(panel);
52958             return panel;
52959         }
52960         var el = panel.getEl();
52961         if(el.dom.parentNode != this.mgr.el.dom){
52962             this.mgr.el.dom.appendChild(el.dom);
52963         }
52964         if(panel.setRegion){
52965             panel.setRegion(this);
52966         }
52967         this.panels.add(panel);
52968         el.setStyle("position", "absolute");
52969         if(!panel.background){
52970             this.setActivePanel(panel);
52971             if(this.config.initialSize && this.panels.getCount()==1){
52972                 this.resizeTo(this.config.initialSize);
52973             }
52974         }
52975         this.fireEvent("paneladded", this, panel);
52976         return panel;
52977     },
52978     
52979     /**
52980      * Returns true if the panel is in this region.
52981      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52982      * @return {Boolean}
52983      */
52984     hasPanel : function(panel){
52985         if(typeof panel == "object"){ // must be panel obj
52986             panel = panel.getId();
52987         }
52988         return this.getPanel(panel) ? true : false;
52989     },
52990     
52991     /**
52992      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52993      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52994      * @param {Boolean} preservePanel Overrides the config preservePanel option
52995      * @return {Roo.ContentPanel} The panel that was removed
52996      */
52997     remove : function(panel, preservePanel){
52998         panel = this.getPanel(panel);
52999         if(!panel){
53000             return null;
53001         }
53002         var e = {};
53003         this.fireEvent("beforeremove", this, panel, e);
53004         if(e.cancel === true){
53005             return null;
53006         }
53007         var panelId = panel.getId();
53008         this.panels.removeKey(panelId);
53009         return panel;
53010     },
53011     
53012     /**
53013      * Returns the panel specified or null if it's not in this region.
53014      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53015      * @return {Roo.ContentPanel}
53016      */
53017     getPanel : function(id){
53018         if(typeof id == "object"){ // must be panel obj
53019             return id;
53020         }
53021         return this.panels.get(id);
53022     },
53023     
53024     /**
53025      * Returns this regions position (north/south/east/west/center).
53026      * @return {String} 
53027      */
53028     getPosition: function(){
53029         return this.position;    
53030     }
53031 });/*
53032  * Based on:
53033  * Ext JS Library 1.1.1
53034  * Copyright(c) 2006-2007, Ext JS, LLC.
53035  *
53036  * Originally Released Under LGPL - original licence link has changed is not relivant.
53037  *
53038  * Fork - LGPL
53039  * <script type="text/javascript">
53040  */
53041  
53042 /**
53043  * @class Roo.LayoutRegion
53044  * @extends Roo.BasicLayoutRegion
53045  * This class represents a region in a layout manager.
53046  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
53047  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
53048  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
53049  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
53050  * @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})
53051  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
53052  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
53053  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
53054  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
53055  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
53056  * @cfg {String}    title           The title for the region (overrides panel titles)
53057  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
53058  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
53059  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
53060  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
53061  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
53062  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
53063  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
53064  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
53065  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
53066  * @cfg {Boolean}   showPin         True to show a pin button
53067  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
53068  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
53069  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
53070  * @cfg {Number}    width           For East/West panels
53071  * @cfg {Number}    height          For North/South panels
53072  * @cfg {Boolean}   split           To show the splitter
53073  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
53074  */
53075 Roo.LayoutRegion = function(mgr, config, pos){
53076     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
53077     var dh = Roo.DomHelper;
53078     /** This region's container element 
53079     * @type Roo.Element */
53080     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
53081     /** This region's title element 
53082     * @type Roo.Element */
53083
53084     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
53085         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
53086         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
53087     ]}, true);
53088     this.titleEl.enableDisplayMode();
53089     /** This region's title text element 
53090     * @type HTMLElement */
53091     this.titleTextEl = this.titleEl.dom.firstChild;
53092     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
53093     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
53094     this.closeBtn.enableDisplayMode();
53095     this.closeBtn.on("click", this.closeClicked, this);
53096     this.closeBtn.hide();
53097
53098     this.createBody(config);
53099     this.visible = true;
53100     this.collapsed = false;
53101
53102     if(config.hideWhenEmpty){
53103         this.hide();
53104         this.on("paneladded", this.validateVisibility, this);
53105         this.on("panelremoved", this.validateVisibility, this);
53106     }
53107     this.applyConfig(config);
53108 };
53109
53110 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
53111
53112     createBody : function(){
53113         /** This region's body element 
53114         * @type Roo.Element */
53115         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
53116     },
53117
53118     applyConfig : function(c){
53119         if(c.collapsible && this.position != "center" && !this.collapsedEl){
53120             var dh = Roo.DomHelper;
53121             if(c.titlebar !== false){
53122                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
53123                 this.collapseBtn.on("click", this.collapse, this);
53124                 this.collapseBtn.enableDisplayMode();
53125
53126                 if(c.showPin === true || this.showPin){
53127                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
53128                     this.stickBtn.enableDisplayMode();
53129                     this.stickBtn.on("click", this.expand, this);
53130                     this.stickBtn.hide();
53131                 }
53132             }
53133             /** This region's collapsed element
53134             * @type Roo.Element */
53135             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
53136                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
53137             ]}, true);
53138             if(c.floatable !== false){
53139                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
53140                this.collapsedEl.on("click", this.collapseClick, this);
53141             }
53142
53143             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
53144                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
53145                    id: "message", unselectable: "on", style:{"float":"left"}});
53146                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
53147              }
53148             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
53149             this.expandBtn.on("click", this.expand, this);
53150         }
53151         if(this.collapseBtn){
53152             this.collapseBtn.setVisible(c.collapsible == true);
53153         }
53154         this.cmargins = c.cmargins || this.cmargins ||
53155                          (this.position == "west" || this.position == "east" ?
53156                              {top: 0, left: 2, right:2, bottom: 0} :
53157                              {top: 2, left: 0, right:0, bottom: 2});
53158         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53159         this.bottomTabs = c.tabPosition != "top";
53160         this.autoScroll = c.autoScroll || false;
53161         if(this.autoScroll){
53162             this.bodyEl.setStyle("overflow", "auto");
53163         }else{
53164             this.bodyEl.setStyle("overflow", "hidden");
53165         }
53166         //if(c.titlebar !== false){
53167             if((!c.titlebar && !c.title) || c.titlebar === false){
53168                 this.titleEl.hide();
53169             }else{
53170                 this.titleEl.show();
53171                 if(c.title){
53172                     this.titleTextEl.innerHTML = c.title;
53173                 }
53174             }
53175         //}
53176         this.duration = c.duration || .30;
53177         this.slideDuration = c.slideDuration || .45;
53178         this.config = c;
53179         if(c.collapsed){
53180             this.collapse(true);
53181         }
53182         if(c.hidden){
53183             this.hide();
53184         }
53185     },
53186     /**
53187      * Returns true if this region is currently visible.
53188      * @return {Boolean}
53189      */
53190     isVisible : function(){
53191         return this.visible;
53192     },
53193
53194     /**
53195      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
53196      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
53197      */
53198     setCollapsedTitle : function(title){
53199         title = title || "&#160;";
53200         if(this.collapsedTitleTextEl){
53201             this.collapsedTitleTextEl.innerHTML = title;
53202         }
53203     },
53204
53205     getBox : function(){
53206         var b;
53207         if(!this.collapsed){
53208             b = this.el.getBox(false, true);
53209         }else{
53210             b = this.collapsedEl.getBox(false, true);
53211         }
53212         return b;
53213     },
53214
53215     getMargins : function(){
53216         return this.collapsed ? this.cmargins : this.margins;
53217     },
53218
53219     highlight : function(){
53220         this.el.addClass("x-layout-panel-dragover");
53221     },
53222
53223     unhighlight : function(){
53224         this.el.removeClass("x-layout-panel-dragover");
53225     },
53226
53227     updateBox : function(box){
53228         this.box = box;
53229         if(!this.collapsed){
53230             this.el.dom.style.left = box.x + "px";
53231             this.el.dom.style.top = box.y + "px";
53232             this.updateBody(box.width, box.height);
53233         }else{
53234             this.collapsedEl.dom.style.left = box.x + "px";
53235             this.collapsedEl.dom.style.top = box.y + "px";
53236             this.collapsedEl.setSize(box.width, box.height);
53237         }
53238         if(this.tabs){
53239             this.tabs.autoSizeTabs();
53240         }
53241     },
53242
53243     updateBody : function(w, h){
53244         if(w !== null){
53245             this.el.setWidth(w);
53246             w -= this.el.getBorderWidth("rl");
53247             if(this.config.adjustments){
53248                 w += this.config.adjustments[0];
53249             }
53250         }
53251         if(h !== null){
53252             this.el.setHeight(h);
53253             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
53254             h -= this.el.getBorderWidth("tb");
53255             if(this.config.adjustments){
53256                 h += this.config.adjustments[1];
53257             }
53258             this.bodyEl.setHeight(h);
53259             if(this.tabs){
53260                 h = this.tabs.syncHeight(h);
53261             }
53262         }
53263         if(this.panelSize){
53264             w = w !== null ? w : this.panelSize.width;
53265             h = h !== null ? h : this.panelSize.height;
53266         }
53267         if(this.activePanel){
53268             var el = this.activePanel.getEl();
53269             w = w !== null ? w : el.getWidth();
53270             h = h !== null ? h : el.getHeight();
53271             this.panelSize = {width: w, height: h};
53272             this.activePanel.setSize(w, h);
53273         }
53274         if(Roo.isIE && this.tabs){
53275             this.tabs.el.repaint();
53276         }
53277     },
53278
53279     /**
53280      * Returns the container element for this region.
53281      * @return {Roo.Element}
53282      */
53283     getEl : function(){
53284         return this.el;
53285     },
53286
53287     /**
53288      * Hides this region.
53289      */
53290     hide : function(){
53291         if(!this.collapsed){
53292             this.el.dom.style.left = "-2000px";
53293             this.el.hide();
53294         }else{
53295             this.collapsedEl.dom.style.left = "-2000px";
53296             this.collapsedEl.hide();
53297         }
53298         this.visible = false;
53299         this.fireEvent("visibilitychange", this, false);
53300     },
53301
53302     /**
53303      * Shows this region if it was previously hidden.
53304      */
53305     show : function(){
53306         if(!this.collapsed){
53307             this.el.show();
53308         }else{
53309             this.collapsedEl.show();
53310         }
53311         this.visible = true;
53312         this.fireEvent("visibilitychange", this, true);
53313     },
53314
53315     closeClicked : function(){
53316         if(this.activePanel){
53317             this.remove(this.activePanel);
53318         }
53319     },
53320
53321     collapseClick : function(e){
53322         if(this.isSlid){
53323            e.stopPropagation();
53324            this.slideIn();
53325         }else{
53326            e.stopPropagation();
53327            this.slideOut();
53328         }
53329     },
53330
53331     /**
53332      * Collapses this region.
53333      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
53334      */
53335     collapse : function(skipAnim, skipCheck){
53336         if(this.collapsed) {
53337             return;
53338         }
53339         
53340         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53341             
53342             this.collapsed = true;
53343             if(this.split){
53344                 this.split.el.hide();
53345             }
53346             if(this.config.animate && skipAnim !== true){
53347                 this.fireEvent("invalidated", this);
53348                 this.animateCollapse();
53349             }else{
53350                 this.el.setLocation(-20000,-20000);
53351                 this.el.hide();
53352                 this.collapsedEl.show();
53353                 this.fireEvent("collapsed", this);
53354                 this.fireEvent("invalidated", this);
53355             }
53356         }
53357         
53358     },
53359
53360     animateCollapse : function(){
53361         // overridden
53362     },
53363
53364     /**
53365      * Expands this region if it was previously collapsed.
53366      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53367      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53368      */
53369     expand : function(e, skipAnim){
53370         if(e) {
53371             e.stopPropagation();
53372         }
53373         if(!this.collapsed || this.el.hasActiveFx()) {
53374             return;
53375         }
53376         if(this.isSlid){
53377             this.afterSlideIn();
53378             skipAnim = true;
53379         }
53380         this.collapsed = false;
53381         if(this.config.animate && skipAnim !== true){
53382             this.animateExpand();
53383         }else{
53384             this.el.show();
53385             if(this.split){
53386                 this.split.el.show();
53387             }
53388             this.collapsedEl.setLocation(-2000,-2000);
53389             this.collapsedEl.hide();
53390             this.fireEvent("invalidated", this);
53391             this.fireEvent("expanded", this);
53392         }
53393     },
53394
53395     animateExpand : function(){
53396         // overridden
53397     },
53398
53399     initTabs : function()
53400     {
53401         this.bodyEl.setStyle("overflow", "hidden");
53402         var ts = new Roo.TabPanel(
53403                 this.bodyEl.dom,
53404                 {
53405                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53406                     disableTooltips: this.config.disableTabTips,
53407                     toolbar : this.config.toolbar
53408                 }
53409         );
53410         if(this.config.hideTabs){
53411             ts.stripWrap.setDisplayed(false);
53412         }
53413         this.tabs = ts;
53414         ts.resizeTabs = this.config.resizeTabs === true;
53415         ts.minTabWidth = this.config.minTabWidth || 40;
53416         ts.maxTabWidth = this.config.maxTabWidth || 250;
53417         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53418         ts.monitorResize = false;
53419         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53420         ts.bodyEl.addClass('x-layout-tabs-body');
53421         this.panels.each(this.initPanelAsTab, this);
53422     },
53423
53424     initPanelAsTab : function(panel){
53425         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53426                     this.config.closeOnTab && panel.isClosable());
53427         if(panel.tabTip !== undefined){
53428             ti.setTooltip(panel.tabTip);
53429         }
53430         ti.on("activate", function(){
53431               this.setActivePanel(panel);
53432         }, this);
53433         if(this.config.closeOnTab){
53434             ti.on("beforeclose", function(t, e){
53435                 e.cancel = true;
53436                 this.remove(panel);
53437             }, this);
53438         }
53439         return ti;
53440     },
53441
53442     updatePanelTitle : function(panel, title){
53443         if(this.activePanel == panel){
53444             this.updateTitle(title);
53445         }
53446         if(this.tabs){
53447             var ti = this.tabs.getTab(panel.getEl().id);
53448             ti.setText(title);
53449             if(panel.tabTip !== undefined){
53450                 ti.setTooltip(panel.tabTip);
53451             }
53452         }
53453     },
53454
53455     updateTitle : function(title){
53456         if(this.titleTextEl && !this.config.title){
53457             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53458         }
53459     },
53460
53461     setActivePanel : function(panel){
53462         panel = this.getPanel(panel);
53463         if(this.activePanel && this.activePanel != panel){
53464             this.activePanel.setActiveState(false);
53465         }
53466         this.activePanel = panel;
53467         panel.setActiveState(true);
53468         if(this.panelSize){
53469             panel.setSize(this.panelSize.width, this.panelSize.height);
53470         }
53471         if(this.closeBtn){
53472             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53473         }
53474         this.updateTitle(panel.getTitle());
53475         if(this.tabs){
53476             this.fireEvent("invalidated", this);
53477         }
53478         this.fireEvent("panelactivated", this, panel);
53479     },
53480
53481     /**
53482      * Shows the specified panel.
53483      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53484      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53485      */
53486     showPanel : function(panel)
53487     {
53488         panel = this.getPanel(panel);
53489         if(panel){
53490             if(this.tabs){
53491                 var tab = this.tabs.getTab(panel.getEl().id);
53492                 if(tab.isHidden()){
53493                     this.tabs.unhideTab(tab.id);
53494                 }
53495                 tab.activate();
53496             }else{
53497                 this.setActivePanel(panel);
53498             }
53499         }
53500         return panel;
53501     },
53502
53503     /**
53504      * Get the active panel for this region.
53505      * @return {Roo.ContentPanel} The active panel or null
53506      */
53507     getActivePanel : function(){
53508         return this.activePanel;
53509     },
53510
53511     validateVisibility : function(){
53512         if(this.panels.getCount() < 1){
53513             this.updateTitle("&#160;");
53514             this.closeBtn.hide();
53515             this.hide();
53516         }else{
53517             if(!this.isVisible()){
53518                 this.show();
53519             }
53520         }
53521     },
53522
53523     /**
53524      * Adds the passed ContentPanel(s) to this region.
53525      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53526      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53527      */
53528     add : function(panel){
53529         if(arguments.length > 1){
53530             for(var i = 0, len = arguments.length; i < len; i++) {
53531                 this.add(arguments[i]);
53532             }
53533             return null;
53534         }
53535         if(this.hasPanel(panel)){
53536             this.showPanel(panel);
53537             return panel;
53538         }
53539         panel.setRegion(this);
53540         this.panels.add(panel);
53541         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53542             this.bodyEl.dom.appendChild(panel.getEl().dom);
53543             if(panel.background !== true){
53544                 this.setActivePanel(panel);
53545             }
53546             this.fireEvent("paneladded", this, panel);
53547             return panel;
53548         }
53549         if(!this.tabs){
53550             this.initTabs();
53551         }else{
53552             this.initPanelAsTab(panel);
53553         }
53554         if(panel.background !== true){
53555             this.tabs.activate(panel.getEl().id);
53556         }
53557         this.fireEvent("paneladded", this, panel);
53558         return panel;
53559     },
53560
53561     /**
53562      * Hides the tab for the specified panel.
53563      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53564      */
53565     hidePanel : function(panel){
53566         if(this.tabs && (panel = this.getPanel(panel))){
53567             this.tabs.hideTab(panel.getEl().id);
53568         }
53569     },
53570
53571     /**
53572      * Unhides the tab for a previously hidden panel.
53573      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53574      */
53575     unhidePanel : function(panel){
53576         if(this.tabs && (panel = this.getPanel(panel))){
53577             this.tabs.unhideTab(panel.getEl().id);
53578         }
53579     },
53580
53581     clearPanels : function(){
53582         while(this.panels.getCount() > 0){
53583              this.remove(this.panels.first());
53584         }
53585     },
53586
53587     /**
53588      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53589      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53590      * @param {Boolean} preservePanel Overrides the config preservePanel option
53591      * @return {Roo.ContentPanel} The panel that was removed
53592      */
53593     remove : function(panel, preservePanel){
53594         panel = this.getPanel(panel);
53595         if(!panel){
53596             return null;
53597         }
53598         var e = {};
53599         this.fireEvent("beforeremove", this, panel, e);
53600         if(e.cancel === true){
53601             return null;
53602         }
53603         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53604         var panelId = panel.getId();
53605         this.panels.removeKey(panelId);
53606         if(preservePanel){
53607             document.body.appendChild(panel.getEl().dom);
53608         }
53609         if(this.tabs){
53610             this.tabs.removeTab(panel.getEl().id);
53611         }else if (!preservePanel){
53612             this.bodyEl.dom.removeChild(panel.getEl().dom);
53613         }
53614         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53615             var p = this.panels.first();
53616             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53617             tempEl.appendChild(p.getEl().dom);
53618             this.bodyEl.update("");
53619             this.bodyEl.dom.appendChild(p.getEl().dom);
53620             tempEl = null;
53621             this.updateTitle(p.getTitle());
53622             this.tabs = null;
53623             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53624             this.setActivePanel(p);
53625         }
53626         panel.setRegion(null);
53627         if(this.activePanel == panel){
53628             this.activePanel = null;
53629         }
53630         if(this.config.autoDestroy !== false && preservePanel !== true){
53631             try{panel.destroy();}catch(e){}
53632         }
53633         this.fireEvent("panelremoved", this, panel);
53634         return panel;
53635     },
53636
53637     /**
53638      * Returns the TabPanel component used by this region
53639      * @return {Roo.TabPanel}
53640      */
53641     getTabs : function(){
53642         return this.tabs;
53643     },
53644
53645     createTool : function(parentEl, className){
53646         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53647             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53648         btn.addClassOnOver("x-layout-tools-button-over");
53649         return btn;
53650     }
53651 });/*
53652  * Based on:
53653  * Ext JS Library 1.1.1
53654  * Copyright(c) 2006-2007, Ext JS, LLC.
53655  *
53656  * Originally Released Under LGPL - original licence link has changed is not relivant.
53657  *
53658  * Fork - LGPL
53659  * <script type="text/javascript">
53660  */
53661  
53662
53663
53664 /**
53665  * @class Roo.SplitLayoutRegion
53666  * @extends Roo.LayoutRegion
53667  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53668  */
53669 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53670     this.cursor = cursor;
53671     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53672 };
53673
53674 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53675     splitTip : "Drag to resize.",
53676     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53677     useSplitTips : false,
53678
53679     applyConfig : function(config){
53680         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53681         if(config.split){
53682             if(!this.split){
53683                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53684                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53685                 /** The SplitBar for this region 
53686                 * @type Roo.SplitBar */
53687                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53688                 this.split.on("moved", this.onSplitMove, this);
53689                 this.split.useShim = config.useShim === true;
53690                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53691                 if(this.useSplitTips){
53692                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53693                 }
53694                 if(config.collapsible){
53695                     this.split.el.on("dblclick", this.collapse,  this);
53696                 }
53697             }
53698             if(typeof config.minSize != "undefined"){
53699                 this.split.minSize = config.minSize;
53700             }
53701             if(typeof config.maxSize != "undefined"){
53702                 this.split.maxSize = config.maxSize;
53703             }
53704             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53705                 this.hideSplitter();
53706             }
53707         }
53708     },
53709
53710     getHMaxSize : function(){
53711          var cmax = this.config.maxSize || 10000;
53712          var center = this.mgr.getRegion("center");
53713          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53714     },
53715
53716     getVMaxSize : function(){
53717          var cmax = this.config.maxSize || 10000;
53718          var center = this.mgr.getRegion("center");
53719          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53720     },
53721
53722     onSplitMove : function(split, newSize){
53723         this.fireEvent("resized", this, newSize);
53724     },
53725     
53726     /** 
53727      * Returns the {@link Roo.SplitBar} for this region.
53728      * @return {Roo.SplitBar}
53729      */
53730     getSplitBar : function(){
53731         return this.split;
53732     },
53733     
53734     hide : function(){
53735         this.hideSplitter();
53736         Roo.SplitLayoutRegion.superclass.hide.call(this);
53737     },
53738
53739     hideSplitter : function(){
53740         if(this.split){
53741             this.split.el.setLocation(-2000,-2000);
53742             this.split.el.hide();
53743         }
53744     },
53745
53746     show : function(){
53747         if(this.split){
53748             this.split.el.show();
53749         }
53750         Roo.SplitLayoutRegion.superclass.show.call(this);
53751     },
53752     
53753     beforeSlide: function(){
53754         if(Roo.isGecko){// firefox overflow auto bug workaround
53755             this.bodyEl.clip();
53756             if(this.tabs) {
53757                 this.tabs.bodyEl.clip();
53758             }
53759             if(this.activePanel){
53760                 this.activePanel.getEl().clip();
53761                 
53762                 if(this.activePanel.beforeSlide){
53763                     this.activePanel.beforeSlide();
53764                 }
53765             }
53766         }
53767     },
53768     
53769     afterSlide : function(){
53770         if(Roo.isGecko){// firefox overflow auto bug workaround
53771             this.bodyEl.unclip();
53772             if(this.tabs) {
53773                 this.tabs.bodyEl.unclip();
53774             }
53775             if(this.activePanel){
53776                 this.activePanel.getEl().unclip();
53777                 if(this.activePanel.afterSlide){
53778                     this.activePanel.afterSlide();
53779                 }
53780             }
53781         }
53782     },
53783
53784     initAutoHide : function(){
53785         if(this.autoHide !== false){
53786             if(!this.autoHideHd){
53787                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53788                 this.autoHideHd = {
53789                     "mouseout": function(e){
53790                         if(!e.within(this.el, true)){
53791                             st.delay(500);
53792                         }
53793                     },
53794                     "mouseover" : function(e){
53795                         st.cancel();
53796                     },
53797                     scope : this
53798                 };
53799             }
53800             this.el.on(this.autoHideHd);
53801         }
53802     },
53803
53804     clearAutoHide : function(){
53805         if(this.autoHide !== false){
53806             this.el.un("mouseout", this.autoHideHd.mouseout);
53807             this.el.un("mouseover", this.autoHideHd.mouseover);
53808         }
53809     },
53810
53811     clearMonitor : function(){
53812         Roo.get(document).un("click", this.slideInIf, this);
53813     },
53814
53815     // these names are backwards but not changed for compat
53816     slideOut : function(){
53817         if(this.isSlid || this.el.hasActiveFx()){
53818             return;
53819         }
53820         this.isSlid = true;
53821         if(this.collapseBtn){
53822             this.collapseBtn.hide();
53823         }
53824         this.closeBtnState = this.closeBtn.getStyle('display');
53825         this.closeBtn.hide();
53826         if(this.stickBtn){
53827             this.stickBtn.show();
53828         }
53829         this.el.show();
53830         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53831         this.beforeSlide();
53832         this.el.setStyle("z-index", 10001);
53833         this.el.slideIn(this.getSlideAnchor(), {
53834             callback: function(){
53835                 this.afterSlide();
53836                 this.initAutoHide();
53837                 Roo.get(document).on("click", this.slideInIf, this);
53838                 this.fireEvent("slideshow", this);
53839             },
53840             scope: this,
53841             block: true
53842         });
53843     },
53844
53845     afterSlideIn : function(){
53846         this.clearAutoHide();
53847         this.isSlid = false;
53848         this.clearMonitor();
53849         this.el.setStyle("z-index", "");
53850         if(this.collapseBtn){
53851             this.collapseBtn.show();
53852         }
53853         this.closeBtn.setStyle('display', this.closeBtnState);
53854         if(this.stickBtn){
53855             this.stickBtn.hide();
53856         }
53857         this.fireEvent("slidehide", this);
53858     },
53859
53860     slideIn : function(cb){
53861         if(!this.isSlid || this.el.hasActiveFx()){
53862             Roo.callback(cb);
53863             return;
53864         }
53865         this.isSlid = false;
53866         this.beforeSlide();
53867         this.el.slideOut(this.getSlideAnchor(), {
53868             callback: function(){
53869                 this.el.setLeftTop(-10000, -10000);
53870                 this.afterSlide();
53871                 this.afterSlideIn();
53872                 Roo.callback(cb);
53873             },
53874             scope: this,
53875             block: true
53876         });
53877     },
53878     
53879     slideInIf : function(e){
53880         if(!e.within(this.el)){
53881             this.slideIn();
53882         }
53883     },
53884
53885     animateCollapse : function(){
53886         this.beforeSlide();
53887         this.el.setStyle("z-index", 20000);
53888         var anchor = this.getSlideAnchor();
53889         this.el.slideOut(anchor, {
53890             callback : function(){
53891                 this.el.setStyle("z-index", "");
53892                 this.collapsedEl.slideIn(anchor, {duration:.3});
53893                 this.afterSlide();
53894                 this.el.setLocation(-10000,-10000);
53895                 this.el.hide();
53896                 this.fireEvent("collapsed", this);
53897             },
53898             scope: this,
53899             block: true
53900         });
53901     },
53902
53903     animateExpand : function(){
53904         this.beforeSlide();
53905         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53906         this.el.setStyle("z-index", 20000);
53907         this.collapsedEl.hide({
53908             duration:.1
53909         });
53910         this.el.slideIn(this.getSlideAnchor(), {
53911             callback : function(){
53912                 this.el.setStyle("z-index", "");
53913                 this.afterSlide();
53914                 if(this.split){
53915                     this.split.el.show();
53916                 }
53917                 this.fireEvent("invalidated", this);
53918                 this.fireEvent("expanded", this);
53919             },
53920             scope: this,
53921             block: true
53922         });
53923     },
53924
53925     anchors : {
53926         "west" : "left",
53927         "east" : "right",
53928         "north" : "top",
53929         "south" : "bottom"
53930     },
53931
53932     sanchors : {
53933         "west" : "l",
53934         "east" : "r",
53935         "north" : "t",
53936         "south" : "b"
53937     },
53938
53939     canchors : {
53940         "west" : "tl-tr",
53941         "east" : "tr-tl",
53942         "north" : "tl-bl",
53943         "south" : "bl-tl"
53944     },
53945
53946     getAnchor : function(){
53947         return this.anchors[this.position];
53948     },
53949
53950     getCollapseAnchor : function(){
53951         return this.canchors[this.position];
53952     },
53953
53954     getSlideAnchor : function(){
53955         return this.sanchors[this.position];
53956     },
53957
53958     getAlignAdj : function(){
53959         var cm = this.cmargins;
53960         switch(this.position){
53961             case "west":
53962                 return [0, 0];
53963             break;
53964             case "east":
53965                 return [0, 0];
53966             break;
53967             case "north":
53968                 return [0, 0];
53969             break;
53970             case "south":
53971                 return [0, 0];
53972             break;
53973         }
53974     },
53975
53976     getExpandAdj : function(){
53977         var c = this.collapsedEl, cm = this.cmargins;
53978         switch(this.position){
53979             case "west":
53980                 return [-(cm.right+c.getWidth()+cm.left), 0];
53981             break;
53982             case "east":
53983                 return [cm.right+c.getWidth()+cm.left, 0];
53984             break;
53985             case "north":
53986                 return [0, -(cm.top+cm.bottom+c.getHeight())];
53987             break;
53988             case "south":
53989                 return [0, cm.top+cm.bottom+c.getHeight()];
53990             break;
53991         }
53992     }
53993 });/*
53994  * Based on:
53995  * Ext JS Library 1.1.1
53996  * Copyright(c) 2006-2007, Ext JS, LLC.
53997  *
53998  * Originally Released Under LGPL - original licence link has changed is not relivant.
53999  *
54000  * Fork - LGPL
54001  * <script type="text/javascript">
54002  */
54003 /*
54004  * These classes are private internal classes
54005  */
54006 Roo.CenterLayoutRegion = function(mgr, config){
54007     Roo.LayoutRegion.call(this, mgr, config, "center");
54008     this.visible = true;
54009     this.minWidth = config.minWidth || 20;
54010     this.minHeight = config.minHeight || 20;
54011 };
54012
54013 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
54014     hide : function(){
54015         // center panel can't be hidden
54016     },
54017     
54018     show : function(){
54019         // center panel can't be hidden
54020     },
54021     
54022     getMinWidth: function(){
54023         return this.minWidth;
54024     },
54025     
54026     getMinHeight: function(){
54027         return this.minHeight;
54028     }
54029 });
54030
54031
54032 Roo.NorthLayoutRegion = function(mgr, config){
54033     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
54034     if(this.split){
54035         this.split.placement = Roo.SplitBar.TOP;
54036         this.split.orientation = Roo.SplitBar.VERTICAL;
54037         this.split.el.addClass("x-layout-split-v");
54038     }
54039     var size = config.initialSize || config.height;
54040     if(typeof size != "undefined"){
54041         this.el.setHeight(size);
54042     }
54043 };
54044 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
54045     orientation: Roo.SplitBar.VERTICAL,
54046     getBox : function(){
54047         if(this.collapsed){
54048             return this.collapsedEl.getBox();
54049         }
54050         var box = this.el.getBox();
54051         if(this.split){
54052             box.height += this.split.el.getHeight();
54053         }
54054         return box;
54055     },
54056     
54057     updateBox : function(box){
54058         if(this.split && !this.collapsed){
54059             box.height -= this.split.el.getHeight();
54060             this.split.el.setLeft(box.x);
54061             this.split.el.setTop(box.y+box.height);
54062             this.split.el.setWidth(box.width);
54063         }
54064         if(this.collapsed){
54065             this.updateBody(box.width, null);
54066         }
54067         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54068     }
54069 });
54070
54071 Roo.SouthLayoutRegion = function(mgr, config){
54072     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
54073     if(this.split){
54074         this.split.placement = Roo.SplitBar.BOTTOM;
54075         this.split.orientation = Roo.SplitBar.VERTICAL;
54076         this.split.el.addClass("x-layout-split-v");
54077     }
54078     var size = config.initialSize || config.height;
54079     if(typeof size != "undefined"){
54080         this.el.setHeight(size);
54081     }
54082 };
54083 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
54084     orientation: Roo.SplitBar.VERTICAL,
54085     getBox : function(){
54086         if(this.collapsed){
54087             return this.collapsedEl.getBox();
54088         }
54089         var box = this.el.getBox();
54090         if(this.split){
54091             var sh = this.split.el.getHeight();
54092             box.height += sh;
54093             box.y -= sh;
54094         }
54095         return box;
54096     },
54097     
54098     updateBox : function(box){
54099         if(this.split && !this.collapsed){
54100             var sh = this.split.el.getHeight();
54101             box.height -= sh;
54102             box.y += sh;
54103             this.split.el.setLeft(box.x);
54104             this.split.el.setTop(box.y-sh);
54105             this.split.el.setWidth(box.width);
54106         }
54107         if(this.collapsed){
54108             this.updateBody(box.width, null);
54109         }
54110         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54111     }
54112 });
54113
54114 Roo.EastLayoutRegion = function(mgr, config){
54115     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
54116     if(this.split){
54117         this.split.placement = Roo.SplitBar.RIGHT;
54118         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54119         this.split.el.addClass("x-layout-split-h");
54120     }
54121     var size = config.initialSize || config.width;
54122     if(typeof size != "undefined"){
54123         this.el.setWidth(size);
54124     }
54125 };
54126 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
54127     orientation: Roo.SplitBar.HORIZONTAL,
54128     getBox : function(){
54129         if(this.collapsed){
54130             return this.collapsedEl.getBox();
54131         }
54132         var box = this.el.getBox();
54133         if(this.split){
54134             var sw = this.split.el.getWidth();
54135             box.width += sw;
54136             box.x -= sw;
54137         }
54138         return box;
54139     },
54140
54141     updateBox : function(box){
54142         if(this.split && !this.collapsed){
54143             var sw = this.split.el.getWidth();
54144             box.width -= sw;
54145             this.split.el.setLeft(box.x);
54146             this.split.el.setTop(box.y);
54147             this.split.el.setHeight(box.height);
54148             box.x += sw;
54149         }
54150         if(this.collapsed){
54151             this.updateBody(null, box.height);
54152         }
54153         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54154     }
54155 });
54156
54157 Roo.WestLayoutRegion = function(mgr, config){
54158     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
54159     if(this.split){
54160         this.split.placement = Roo.SplitBar.LEFT;
54161         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54162         this.split.el.addClass("x-layout-split-h");
54163     }
54164     var size = config.initialSize || config.width;
54165     if(typeof size != "undefined"){
54166         this.el.setWidth(size);
54167     }
54168 };
54169 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
54170     orientation: Roo.SplitBar.HORIZONTAL,
54171     getBox : function(){
54172         if(this.collapsed){
54173             return this.collapsedEl.getBox();
54174         }
54175         var box = this.el.getBox();
54176         if(this.split){
54177             box.width += this.split.el.getWidth();
54178         }
54179         return box;
54180     },
54181     
54182     updateBox : function(box){
54183         if(this.split && !this.collapsed){
54184             var sw = this.split.el.getWidth();
54185             box.width -= sw;
54186             this.split.el.setLeft(box.x+box.width);
54187             this.split.el.setTop(box.y);
54188             this.split.el.setHeight(box.height);
54189         }
54190         if(this.collapsed){
54191             this.updateBody(null, box.height);
54192         }
54193         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54194     }
54195 });
54196 /*
54197  * Based on:
54198  * Ext JS Library 1.1.1
54199  * Copyright(c) 2006-2007, Ext JS, LLC.
54200  *
54201  * Originally Released Under LGPL - original licence link has changed is not relivant.
54202  *
54203  * Fork - LGPL
54204  * <script type="text/javascript">
54205  */
54206  
54207  
54208 /*
54209  * Private internal class for reading and applying state
54210  */
54211 Roo.LayoutStateManager = function(layout){
54212      // default empty state
54213      this.state = {
54214         north: {},
54215         south: {},
54216         east: {},
54217         west: {}       
54218     };
54219 };
54220
54221 Roo.LayoutStateManager.prototype = {
54222     init : function(layout, provider){
54223         this.provider = provider;
54224         var state = provider.get(layout.id+"-layout-state");
54225         if(state){
54226             var wasUpdating = layout.isUpdating();
54227             if(!wasUpdating){
54228                 layout.beginUpdate();
54229             }
54230             for(var key in state){
54231                 if(typeof state[key] != "function"){
54232                     var rstate = state[key];
54233                     var r = layout.getRegion(key);
54234                     if(r && rstate){
54235                         if(rstate.size){
54236                             r.resizeTo(rstate.size);
54237                         }
54238                         if(rstate.collapsed == true){
54239                             r.collapse(true);
54240                         }else{
54241                             r.expand(null, true);
54242                         }
54243                     }
54244                 }
54245             }
54246             if(!wasUpdating){
54247                 layout.endUpdate();
54248             }
54249             this.state = state; 
54250         }
54251         this.layout = layout;
54252         layout.on("regionresized", this.onRegionResized, this);
54253         layout.on("regioncollapsed", this.onRegionCollapsed, this);
54254         layout.on("regionexpanded", this.onRegionExpanded, this);
54255     },
54256     
54257     storeState : function(){
54258         this.provider.set(this.layout.id+"-layout-state", this.state);
54259     },
54260     
54261     onRegionResized : function(region, newSize){
54262         this.state[region.getPosition()].size = newSize;
54263         this.storeState();
54264     },
54265     
54266     onRegionCollapsed : function(region){
54267         this.state[region.getPosition()].collapsed = true;
54268         this.storeState();
54269     },
54270     
54271     onRegionExpanded : function(region){
54272         this.state[region.getPosition()].collapsed = false;
54273         this.storeState();
54274     }
54275 };/*
54276  * Based on:
54277  * Ext JS Library 1.1.1
54278  * Copyright(c) 2006-2007, Ext JS, LLC.
54279  *
54280  * Originally Released Under LGPL - original licence link has changed is not relivant.
54281  *
54282  * Fork - LGPL
54283  * <script type="text/javascript">
54284  */
54285 /**
54286  * @class Roo.ContentPanel
54287  * @extends Roo.util.Observable
54288  * A basic ContentPanel element.
54289  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
54290  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
54291  * @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
54292  * @cfg {Boolean}   closable      True if the panel can be closed/removed
54293  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
54294  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
54295  * @cfg {Toolbar}   toolbar       A toolbar for this panel
54296  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
54297  * @cfg {String} title          The title for this panel
54298  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
54299  * @cfg {String} url            Calls {@link #setUrl} with this value
54300  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
54301  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
54302  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
54303  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
54304  * @cfg {String}    style  Extra style to add to the content panel 
54305
54306  * @constructor
54307  * Create a new ContentPanel.
54308  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
54309  * @param {String/Object} config A string to set only the title or a config object
54310  * @param {String} content (optional) Set the HTML content for this panel
54311  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
54312  */
54313 Roo.ContentPanel = function(el, config, content){
54314     
54315      
54316     /*
54317     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
54318         config = el;
54319         el = Roo.id();
54320     }
54321     if (config && config.parentLayout) { 
54322         el = config.parentLayout.el.createChild(); 
54323     }
54324     */
54325     if(el.autoCreate){ // xtype is available if this is called from factory
54326         config = el;
54327         el = Roo.id();
54328     }
54329     this.el = Roo.get(el);
54330     if(!this.el && config && config.autoCreate){
54331         if(typeof config.autoCreate == "object"){
54332             if(!config.autoCreate.id){
54333                 config.autoCreate.id = config.id||el;
54334             }
54335             this.el = Roo.DomHelper.append(document.body,
54336                         config.autoCreate, true);
54337         }else{
54338             this.el = Roo.DomHelper.append(document.body,
54339                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54340         }
54341     }
54342     
54343     
54344     this.closable = false;
54345     this.loaded = false;
54346     this.active = false;
54347     if(typeof config == "string"){
54348         this.title = config;
54349     }else{
54350         Roo.apply(this, config);
54351     }
54352     
54353     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54354         this.wrapEl = this.el.wrap();
54355         this.toolbar.container = this.el.insertSibling(false, 'before');
54356         this.toolbar = new Roo.Toolbar(this.toolbar);
54357     }
54358     
54359     // xtype created footer. - not sure if will work as we normally have to render first..
54360     if (this.footer && !this.footer.el && this.footer.xtype) {
54361         if (!this.wrapEl) {
54362             this.wrapEl = this.el.wrap();
54363         }
54364     
54365         this.footer.container = this.wrapEl.createChild();
54366          
54367         this.footer = Roo.factory(this.footer, Roo);
54368         
54369     }
54370     
54371     if(this.resizeEl){
54372         this.resizeEl = Roo.get(this.resizeEl, true);
54373     }else{
54374         this.resizeEl = this.el;
54375     }
54376     // handle view.xtype
54377     
54378  
54379     
54380     
54381     this.addEvents({
54382         /**
54383          * @event activate
54384          * Fires when this panel is activated. 
54385          * @param {Roo.ContentPanel} this
54386          */
54387         "activate" : true,
54388         /**
54389          * @event deactivate
54390          * Fires when this panel is activated. 
54391          * @param {Roo.ContentPanel} this
54392          */
54393         "deactivate" : true,
54394
54395         /**
54396          * @event resize
54397          * Fires when this panel is resized if fitToFrame is true.
54398          * @param {Roo.ContentPanel} this
54399          * @param {Number} width The width after any component adjustments
54400          * @param {Number} height The height after any component adjustments
54401          */
54402         "resize" : true,
54403         
54404          /**
54405          * @event render
54406          * Fires when this tab is created
54407          * @param {Roo.ContentPanel} this
54408          */
54409         "render" : true
54410          
54411         
54412     });
54413     
54414
54415     
54416     
54417     if(this.autoScroll){
54418         this.resizeEl.setStyle("overflow", "auto");
54419     } else {
54420         // fix randome scrolling
54421         this.el.on('scroll', function() {
54422             Roo.log('fix random scolling');
54423             this.scrollTo('top',0); 
54424         });
54425     }
54426     content = content || this.content;
54427     if(content){
54428         this.setContent(content);
54429     }
54430     if(config && config.url){
54431         this.setUrl(this.url, this.params, this.loadOnce);
54432     }
54433     
54434     
54435     
54436     Roo.ContentPanel.superclass.constructor.call(this);
54437     
54438     if (this.view && typeof(this.view.xtype) != 'undefined') {
54439         this.view.el = this.el.appendChild(document.createElement("div"));
54440         this.view = Roo.factory(this.view); 
54441         this.view.render  &&  this.view.render(false, '');  
54442     }
54443     
54444     
54445     this.fireEvent('render', this);
54446 };
54447
54448 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54449     tabTip:'',
54450     setRegion : function(region){
54451         this.region = region;
54452         if(region){
54453            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54454         }else{
54455            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54456         } 
54457     },
54458     
54459     /**
54460      * Returns the toolbar for this Panel if one was configured. 
54461      * @return {Roo.Toolbar} 
54462      */
54463     getToolbar : function(){
54464         return this.toolbar;
54465     },
54466     
54467     setActiveState : function(active){
54468         this.active = active;
54469         if(!active){
54470             this.fireEvent("deactivate", this);
54471         }else{
54472             this.fireEvent("activate", this);
54473         }
54474     },
54475     /**
54476      * Updates this panel's element
54477      * @param {String} content The new content
54478      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54479     */
54480     setContent : function(content, loadScripts){
54481         this.el.update(content, loadScripts);
54482     },
54483
54484     ignoreResize : function(w, h){
54485         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54486             return true;
54487         }else{
54488             this.lastSize = {width: w, height: h};
54489             return false;
54490         }
54491     },
54492     /**
54493      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54494      * @return {Roo.UpdateManager} The UpdateManager
54495      */
54496     getUpdateManager : function(){
54497         return this.el.getUpdateManager();
54498     },
54499      /**
54500      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54501      * @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:
54502 <pre><code>
54503 panel.load({
54504     url: "your-url.php",
54505     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54506     callback: yourFunction,
54507     scope: yourObject, //(optional scope)
54508     discardUrl: false,
54509     nocache: false,
54510     text: "Loading...",
54511     timeout: 30,
54512     scripts: false
54513 });
54514 </code></pre>
54515      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54516      * 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.
54517      * @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}
54518      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54519      * @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.
54520      * @return {Roo.ContentPanel} this
54521      */
54522     load : function(){
54523         var um = this.el.getUpdateManager();
54524         um.update.apply(um, arguments);
54525         return this;
54526     },
54527
54528
54529     /**
54530      * 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.
54531      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54532      * @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)
54533      * @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)
54534      * @return {Roo.UpdateManager} The UpdateManager
54535      */
54536     setUrl : function(url, params, loadOnce){
54537         if(this.refreshDelegate){
54538             this.removeListener("activate", this.refreshDelegate);
54539         }
54540         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54541         this.on("activate", this.refreshDelegate);
54542         return this.el.getUpdateManager();
54543     },
54544     
54545     _handleRefresh : function(url, params, loadOnce){
54546         if(!loadOnce || !this.loaded){
54547             var updater = this.el.getUpdateManager();
54548             updater.update(url, params, this._setLoaded.createDelegate(this));
54549         }
54550     },
54551     
54552     _setLoaded : function(){
54553         this.loaded = true;
54554     }, 
54555     
54556     /**
54557      * Returns this panel's id
54558      * @return {String} 
54559      */
54560     getId : function(){
54561         return this.el.id;
54562     },
54563     
54564     /** 
54565      * Returns this panel's element - used by regiosn to add.
54566      * @return {Roo.Element} 
54567      */
54568     getEl : function(){
54569         return this.wrapEl || this.el;
54570     },
54571     
54572     adjustForComponents : function(width, height)
54573     {
54574         //Roo.log('adjustForComponents ');
54575         if(this.resizeEl != this.el){
54576             width -= this.el.getFrameWidth('lr');
54577             height -= this.el.getFrameWidth('tb');
54578         }
54579         if(this.toolbar){
54580             var te = this.toolbar.getEl();
54581             height -= te.getHeight();
54582             te.setWidth(width);
54583         }
54584         if(this.footer){
54585             var te = this.footer.getEl();
54586             //Roo.log("footer:" + te.getHeight());
54587             
54588             height -= te.getHeight();
54589             te.setWidth(width);
54590         }
54591         
54592         
54593         if(this.adjustments){
54594             width += this.adjustments[0];
54595             height += this.adjustments[1];
54596         }
54597         return {"width": width, "height": height};
54598     },
54599     
54600     setSize : function(width, height){
54601         if(this.fitToFrame && !this.ignoreResize(width, height)){
54602             if(this.fitContainer && this.resizeEl != this.el){
54603                 this.el.setSize(width, height);
54604             }
54605             var size = this.adjustForComponents(width, height);
54606             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54607             this.fireEvent('resize', this, size.width, size.height);
54608         }
54609     },
54610     
54611     /**
54612      * Returns this panel's title
54613      * @return {String} 
54614      */
54615     getTitle : function(){
54616         return this.title;
54617     },
54618     
54619     /**
54620      * Set this panel's title
54621      * @param {String} title
54622      */
54623     setTitle : function(title){
54624         this.title = title;
54625         if(this.region){
54626             this.region.updatePanelTitle(this, title);
54627         }
54628     },
54629     
54630     /**
54631      * Returns true is this panel was configured to be closable
54632      * @return {Boolean} 
54633      */
54634     isClosable : function(){
54635         return this.closable;
54636     },
54637     
54638     beforeSlide : function(){
54639         this.el.clip();
54640         this.resizeEl.clip();
54641     },
54642     
54643     afterSlide : function(){
54644         this.el.unclip();
54645         this.resizeEl.unclip();
54646     },
54647     
54648     /**
54649      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54650      *   Will fail silently if the {@link #setUrl} method has not been called.
54651      *   This does not activate the panel, just updates its content.
54652      */
54653     refresh : function(){
54654         if(this.refreshDelegate){
54655            this.loaded = false;
54656            this.refreshDelegate();
54657         }
54658     },
54659     
54660     /**
54661      * Destroys this panel
54662      */
54663     destroy : function(){
54664         this.el.removeAllListeners();
54665         var tempEl = document.createElement("span");
54666         tempEl.appendChild(this.el.dom);
54667         tempEl.innerHTML = "";
54668         this.el.remove();
54669         this.el = null;
54670     },
54671     
54672     /**
54673      * form - if the content panel contains a form - this is a reference to it.
54674      * @type {Roo.form.Form}
54675      */
54676     form : false,
54677     /**
54678      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54679      *    This contains a reference to it.
54680      * @type {Roo.View}
54681      */
54682     view : false,
54683     
54684       /**
54685      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54686      * <pre><code>
54687
54688 layout.addxtype({
54689        xtype : 'Form',
54690        items: [ .... ]
54691    }
54692 );
54693
54694 </code></pre>
54695      * @param {Object} cfg Xtype definition of item to add.
54696      */
54697     
54698     addxtype : function(cfg) {
54699         // add form..
54700         if (cfg.xtype.match(/^Form$/)) {
54701             
54702             var el;
54703             //if (this.footer) {
54704             //    el = this.footer.container.insertSibling(false, 'before');
54705             //} else {
54706                 el = this.el.createChild();
54707             //}
54708
54709             this.form = new  Roo.form.Form(cfg);
54710             
54711             
54712             if ( this.form.allItems.length) {
54713                 this.form.render(el.dom);
54714             }
54715             return this.form;
54716         }
54717         // should only have one of theses..
54718         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54719             // views.. should not be just added - used named prop 'view''
54720             
54721             cfg.el = this.el.appendChild(document.createElement("div"));
54722             // factory?
54723             
54724             var ret = new Roo.factory(cfg);
54725              
54726              ret.render && ret.render(false, ''); // render blank..
54727             this.view = ret;
54728             return ret;
54729         }
54730         return false;
54731     }
54732 });
54733
54734 /**
54735  * @class Roo.GridPanel
54736  * @extends Roo.ContentPanel
54737  * @constructor
54738  * Create a new GridPanel.
54739  * @param {Roo.grid.Grid} grid The grid for this panel
54740  * @param {String/Object} config A string to set only the panel's title, or a config object
54741  */
54742 Roo.GridPanel = function(grid, config){
54743     
54744   
54745     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54746         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54747         
54748     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54749     
54750     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54751     
54752     if(this.toolbar){
54753         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54754     }
54755     // xtype created footer. - not sure if will work as we normally have to render first..
54756     if (this.footer && !this.footer.el && this.footer.xtype) {
54757         
54758         this.footer.container = this.grid.getView().getFooterPanel(true);
54759         this.footer.dataSource = this.grid.dataSource;
54760         this.footer = Roo.factory(this.footer, Roo);
54761         
54762     }
54763     
54764     grid.monitorWindowResize = false; // turn off autosizing
54765     grid.autoHeight = false;
54766     grid.autoWidth = false;
54767     this.grid = grid;
54768     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54769 };
54770
54771 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54772     getId : function(){
54773         return this.grid.id;
54774     },
54775     
54776     /**
54777      * Returns the grid for this panel
54778      * @return {Roo.grid.Grid} 
54779      */
54780     getGrid : function(){
54781         return this.grid;    
54782     },
54783     
54784     setSize : function(width, height){
54785         if(!this.ignoreResize(width, height)){
54786             var grid = this.grid;
54787             var size = this.adjustForComponents(width, height);
54788             grid.getGridEl().setSize(size.width, size.height);
54789             grid.autoSize();
54790         }
54791     },
54792     
54793     beforeSlide : function(){
54794         this.grid.getView().scroller.clip();
54795     },
54796     
54797     afterSlide : function(){
54798         this.grid.getView().scroller.unclip();
54799     },
54800     
54801     destroy : function(){
54802         this.grid.destroy();
54803         delete this.grid;
54804         Roo.GridPanel.superclass.destroy.call(this); 
54805     }
54806 });
54807
54808
54809 /**
54810  * @class Roo.NestedLayoutPanel
54811  * @extends Roo.ContentPanel
54812  * @constructor
54813  * Create a new NestedLayoutPanel.
54814  * 
54815  * 
54816  * @param {Roo.BorderLayout} layout The layout for this panel
54817  * @param {String/Object} config A string to set only the title or a config object
54818  */
54819 Roo.NestedLayoutPanel = function(layout, config)
54820 {
54821     // construct with only one argument..
54822     /* FIXME - implement nicer consturctors
54823     if (layout.layout) {
54824         config = layout;
54825         layout = config.layout;
54826         delete config.layout;
54827     }
54828     if (layout.xtype && !layout.getEl) {
54829         // then layout needs constructing..
54830         layout = Roo.factory(layout, Roo);
54831     }
54832     */
54833     
54834     
54835     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54836     
54837     layout.monitorWindowResize = false; // turn off autosizing
54838     this.layout = layout;
54839     this.layout.getEl().addClass("x-layout-nested-layout");
54840     
54841     
54842     
54843     
54844 };
54845
54846 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54847
54848     setSize : function(width, height){
54849         if(!this.ignoreResize(width, height)){
54850             var size = this.adjustForComponents(width, height);
54851             var el = this.layout.getEl();
54852             el.setSize(size.width, size.height);
54853             var touch = el.dom.offsetWidth;
54854             this.layout.layout();
54855             // ie requires a double layout on the first pass
54856             if(Roo.isIE && !this.initialized){
54857                 this.initialized = true;
54858                 this.layout.layout();
54859             }
54860         }
54861     },
54862     
54863     // activate all subpanels if not currently active..
54864     
54865     setActiveState : function(active){
54866         this.active = active;
54867         if(!active){
54868             this.fireEvent("deactivate", this);
54869             return;
54870         }
54871         
54872         this.fireEvent("activate", this);
54873         // not sure if this should happen before or after..
54874         if (!this.layout) {
54875             return; // should not happen..
54876         }
54877         var reg = false;
54878         for (var r in this.layout.regions) {
54879             reg = this.layout.getRegion(r);
54880             if (reg.getActivePanel()) {
54881                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54882                 reg.setActivePanel(reg.getActivePanel());
54883                 continue;
54884             }
54885             if (!reg.panels.length) {
54886                 continue;
54887             }
54888             reg.showPanel(reg.getPanel(0));
54889         }
54890         
54891         
54892         
54893         
54894     },
54895     
54896     /**
54897      * Returns the nested BorderLayout for this panel
54898      * @return {Roo.BorderLayout} 
54899      */
54900     getLayout : function(){
54901         return this.layout;
54902     },
54903     
54904      /**
54905      * Adds a xtype elements to the layout of the nested panel
54906      * <pre><code>
54907
54908 panel.addxtype({
54909        xtype : 'ContentPanel',
54910        region: 'west',
54911        items: [ .... ]
54912    }
54913 );
54914
54915 panel.addxtype({
54916         xtype : 'NestedLayoutPanel',
54917         region: 'west',
54918         layout: {
54919            center: { },
54920            west: { }   
54921         },
54922         items : [ ... list of content panels or nested layout panels.. ]
54923    }
54924 );
54925 </code></pre>
54926      * @param {Object} cfg Xtype definition of item to add.
54927      */
54928     addxtype : function(cfg) {
54929         return this.layout.addxtype(cfg);
54930     
54931     }
54932 });
54933
54934 Roo.ScrollPanel = function(el, config, content){
54935     config = config || {};
54936     config.fitToFrame = true;
54937     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
54938     
54939     this.el.dom.style.overflow = "hidden";
54940     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
54941     this.el.removeClass("x-layout-inactive-content");
54942     this.el.on("mousewheel", this.onWheel, this);
54943
54944     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
54945     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
54946     up.unselectable(); down.unselectable();
54947     up.on("click", this.scrollUp, this);
54948     down.on("click", this.scrollDown, this);
54949     up.addClassOnOver("x-scroller-btn-over");
54950     down.addClassOnOver("x-scroller-btn-over");
54951     up.addClassOnClick("x-scroller-btn-click");
54952     down.addClassOnClick("x-scroller-btn-click");
54953     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
54954
54955     this.resizeEl = this.el;
54956     this.el = wrap; this.up = up; this.down = down;
54957 };
54958
54959 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
54960     increment : 100,
54961     wheelIncrement : 5,
54962     scrollUp : function(){
54963         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
54964     },
54965
54966     scrollDown : function(){
54967         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
54968     },
54969
54970     afterScroll : function(){
54971         var el = this.resizeEl;
54972         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
54973         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54974         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54975     },
54976
54977     setSize : function(){
54978         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
54979         this.afterScroll();
54980     },
54981
54982     onWheel : function(e){
54983         var d = e.getWheelDelta();
54984         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
54985         this.afterScroll();
54986         e.stopEvent();
54987     },
54988
54989     setContent : function(content, loadScripts){
54990         this.resizeEl.update(content, loadScripts);
54991     }
54992
54993 });
54994
54995
54996
54997
54998
54999
55000
55001
55002
55003 /**
55004  * @class Roo.TreePanel
55005  * @extends Roo.ContentPanel
55006  * @constructor
55007  * Create a new TreePanel. - defaults to fit/scoll contents.
55008  * @param {String/Object} config A string to set only the panel's title, or a config object
55009  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
55010  */
55011 Roo.TreePanel = function(config){
55012     var el = config.el;
55013     var tree = config.tree;
55014     delete config.tree; 
55015     delete config.el; // hopefull!
55016     
55017     // wrapper for IE7 strict & safari scroll issue
55018     
55019     var treeEl = el.createChild();
55020     config.resizeEl = treeEl;
55021     
55022     
55023     
55024     Roo.TreePanel.superclass.constructor.call(this, el, config);
55025  
55026  
55027     this.tree = new Roo.tree.TreePanel(treeEl , tree);
55028     //console.log(tree);
55029     this.on('activate', function()
55030     {
55031         if (this.tree.rendered) {
55032             return;
55033         }
55034         //console.log('render tree');
55035         this.tree.render();
55036     });
55037     // this should not be needed.. - it's actually the 'el' that resizes?
55038     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
55039     
55040     //this.on('resize',  function (cp, w, h) {
55041     //        this.tree.innerCt.setWidth(w);
55042     //        this.tree.innerCt.setHeight(h);
55043     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
55044     //});
55045
55046         
55047     
55048 };
55049
55050 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
55051     fitToFrame : true,
55052     autoScroll : true
55053 });
55054
55055
55056
55057
55058
55059
55060
55061
55062
55063
55064
55065 /*
55066  * Based on:
55067  * Ext JS Library 1.1.1
55068  * Copyright(c) 2006-2007, Ext JS, LLC.
55069  *
55070  * Originally Released Under LGPL - original licence link has changed is not relivant.
55071  *
55072  * Fork - LGPL
55073  * <script type="text/javascript">
55074  */
55075  
55076
55077 /**
55078  * @class Roo.ReaderLayout
55079  * @extends Roo.BorderLayout
55080  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
55081  * center region containing two nested regions (a top one for a list view and one for item preview below),
55082  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
55083  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
55084  * expedites the setup of the overall layout and regions for this common application style.
55085  * Example:
55086  <pre><code>
55087 var reader = new Roo.ReaderLayout();
55088 var CP = Roo.ContentPanel;  // shortcut for adding
55089
55090 reader.beginUpdate();
55091 reader.add("north", new CP("north", "North"));
55092 reader.add("west", new CP("west", {title: "West"}));
55093 reader.add("east", new CP("east", {title: "East"}));
55094
55095 reader.regions.listView.add(new CP("listView", "List"));
55096 reader.regions.preview.add(new CP("preview", "Preview"));
55097 reader.endUpdate();
55098 </code></pre>
55099 * @constructor
55100 * Create a new ReaderLayout
55101 * @param {Object} config Configuration options
55102 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
55103 * document.body if omitted)
55104 */
55105 Roo.ReaderLayout = function(config, renderTo){
55106     var c = config || {size:{}};
55107     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
55108         north: c.north !== false ? Roo.apply({
55109             split:false,
55110             initialSize: 32,
55111             titlebar: false
55112         }, c.north) : false,
55113         west: c.west !== false ? Roo.apply({
55114             split:true,
55115             initialSize: 200,
55116             minSize: 175,
55117             maxSize: 400,
55118             titlebar: true,
55119             collapsible: true,
55120             animate: true,
55121             margins:{left:5,right:0,bottom:5,top:5},
55122             cmargins:{left:5,right:5,bottom:5,top:5}
55123         }, c.west) : false,
55124         east: c.east !== false ? Roo.apply({
55125             split:true,
55126             initialSize: 200,
55127             minSize: 175,
55128             maxSize: 400,
55129             titlebar: true,
55130             collapsible: true,
55131             animate: true,
55132             margins:{left:0,right:5,bottom:5,top:5},
55133             cmargins:{left:5,right:5,bottom:5,top:5}
55134         }, c.east) : false,
55135         center: Roo.apply({
55136             tabPosition: 'top',
55137             autoScroll:false,
55138             closeOnTab: true,
55139             titlebar:false,
55140             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
55141         }, c.center)
55142     });
55143
55144     this.el.addClass('x-reader');
55145
55146     this.beginUpdate();
55147
55148     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
55149         south: c.preview !== false ? Roo.apply({
55150             split:true,
55151             initialSize: 200,
55152             minSize: 100,
55153             autoScroll:true,
55154             collapsible:true,
55155             titlebar: true,
55156             cmargins:{top:5,left:0, right:0, bottom:0}
55157         }, c.preview) : false,
55158         center: Roo.apply({
55159             autoScroll:false,
55160             titlebar:false,
55161             minHeight:200
55162         }, c.listView)
55163     });
55164     this.add('center', new Roo.NestedLayoutPanel(inner,
55165             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
55166
55167     this.endUpdate();
55168
55169     this.regions.preview = inner.getRegion('south');
55170     this.regions.listView = inner.getRegion('center');
55171 };
55172
55173 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
55174  * Based on:
55175  * Ext JS Library 1.1.1
55176  * Copyright(c) 2006-2007, Ext JS, LLC.
55177  *
55178  * Originally Released Under LGPL - original licence link has changed is not relivant.
55179  *
55180  * Fork - LGPL
55181  * <script type="text/javascript">
55182  */
55183  
55184 /**
55185  * @class Roo.grid.Grid
55186  * @extends Roo.util.Observable
55187  * This class represents the primary interface of a component based grid control.
55188  * <br><br>Usage:<pre><code>
55189  var grid = new Roo.grid.Grid("my-container-id", {
55190      ds: myDataStore,
55191      cm: myColModel,
55192      selModel: mySelectionModel,
55193      autoSizeColumns: true,
55194      monitorWindowResize: false,
55195      trackMouseOver: true
55196  });
55197  // set any options
55198  grid.render();
55199  * </code></pre>
55200  * <b>Common Problems:</b><br/>
55201  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
55202  * element will correct this<br/>
55203  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
55204  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
55205  * are unpredictable.<br/>
55206  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
55207  * grid to calculate dimensions/offsets.<br/>
55208   * @constructor
55209  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55210  * The container MUST have some type of size defined for the grid to fill. The container will be
55211  * automatically set to position relative if it isn't already.
55212  * @param {Object} config A config object that sets properties on this grid.
55213  */
55214 Roo.grid.Grid = function(container, config){
55215         // initialize the container
55216         this.container = Roo.get(container);
55217         this.container.update("");
55218         this.container.setStyle("overflow", "hidden");
55219     this.container.addClass('x-grid-container');
55220
55221     this.id = this.container.id;
55222
55223     Roo.apply(this, config);
55224     // check and correct shorthanded configs
55225     if(this.ds){
55226         this.dataSource = this.ds;
55227         delete this.ds;
55228     }
55229     if(this.cm){
55230         this.colModel = this.cm;
55231         delete this.cm;
55232     }
55233     if(this.sm){
55234         this.selModel = this.sm;
55235         delete this.sm;
55236     }
55237
55238     if (this.selModel) {
55239         this.selModel = Roo.factory(this.selModel, Roo.grid);
55240         this.sm = this.selModel;
55241         this.sm.xmodule = this.xmodule || false;
55242     }
55243     if (typeof(this.colModel.config) == 'undefined') {
55244         this.colModel = new Roo.grid.ColumnModel(this.colModel);
55245         this.cm = this.colModel;
55246         this.cm.xmodule = this.xmodule || false;
55247     }
55248     if (this.dataSource) {
55249         this.dataSource= Roo.factory(this.dataSource, Roo.data);
55250         this.ds = this.dataSource;
55251         this.ds.xmodule = this.xmodule || false;
55252          
55253     }
55254     
55255     
55256     
55257     if(this.width){
55258         this.container.setWidth(this.width);
55259     }
55260
55261     if(this.height){
55262         this.container.setHeight(this.height);
55263     }
55264     /** @private */
55265         this.addEvents({
55266         // raw events
55267         /**
55268          * @event click
55269          * The raw click event for the entire grid.
55270          * @param {Roo.EventObject} e
55271          */
55272         "click" : true,
55273         /**
55274          * @event dblclick
55275          * The raw dblclick event for the entire grid.
55276          * @param {Roo.EventObject} e
55277          */
55278         "dblclick" : true,
55279         /**
55280          * @event contextmenu
55281          * The raw contextmenu event for the entire grid.
55282          * @param {Roo.EventObject} e
55283          */
55284         "contextmenu" : true,
55285         /**
55286          * @event mousedown
55287          * The raw mousedown event for the entire grid.
55288          * @param {Roo.EventObject} e
55289          */
55290         "mousedown" : true,
55291         /**
55292          * @event mouseup
55293          * The raw mouseup event for the entire grid.
55294          * @param {Roo.EventObject} e
55295          */
55296         "mouseup" : true,
55297         /**
55298          * @event mouseover
55299          * The raw mouseover event for the entire grid.
55300          * @param {Roo.EventObject} e
55301          */
55302         "mouseover" : true,
55303         /**
55304          * @event mouseout
55305          * The raw mouseout event for the entire grid.
55306          * @param {Roo.EventObject} e
55307          */
55308         "mouseout" : true,
55309         /**
55310          * @event keypress
55311          * The raw keypress event for the entire grid.
55312          * @param {Roo.EventObject} e
55313          */
55314         "keypress" : true,
55315         /**
55316          * @event keydown
55317          * The raw keydown event for the entire grid.
55318          * @param {Roo.EventObject} e
55319          */
55320         "keydown" : true,
55321
55322         // custom events
55323
55324         /**
55325          * @event cellclick
55326          * Fires when a cell is clicked
55327          * @param {Grid} this
55328          * @param {Number} rowIndex
55329          * @param {Number} columnIndex
55330          * @param {Roo.EventObject} e
55331          */
55332         "cellclick" : true,
55333         /**
55334          * @event celldblclick
55335          * Fires when a cell is double clicked
55336          * @param {Grid} this
55337          * @param {Number} rowIndex
55338          * @param {Number} columnIndex
55339          * @param {Roo.EventObject} e
55340          */
55341         "celldblclick" : true,
55342         /**
55343          * @event rowclick
55344          * Fires when a row is clicked
55345          * @param {Grid} this
55346          * @param {Number} rowIndex
55347          * @param {Roo.EventObject} e
55348          */
55349         "rowclick" : true,
55350         /**
55351          * @event rowdblclick
55352          * Fires when a row is double clicked
55353          * @param {Grid} this
55354          * @param {Number} rowIndex
55355          * @param {Roo.EventObject} e
55356          */
55357         "rowdblclick" : true,
55358         /**
55359          * @event headerclick
55360          * Fires when a header is clicked
55361          * @param {Grid} this
55362          * @param {Number} columnIndex
55363          * @param {Roo.EventObject} e
55364          */
55365         "headerclick" : true,
55366         /**
55367          * @event headerdblclick
55368          * Fires when a header cell is double clicked
55369          * @param {Grid} this
55370          * @param {Number} columnIndex
55371          * @param {Roo.EventObject} e
55372          */
55373         "headerdblclick" : true,
55374         /**
55375          * @event rowcontextmenu
55376          * Fires when a row is right clicked
55377          * @param {Grid} this
55378          * @param {Number} rowIndex
55379          * @param {Roo.EventObject} e
55380          */
55381         "rowcontextmenu" : true,
55382         /**
55383          * @event cellcontextmenu
55384          * Fires when a cell is right clicked
55385          * @param {Grid} this
55386          * @param {Number} rowIndex
55387          * @param {Number} cellIndex
55388          * @param {Roo.EventObject} e
55389          */
55390          "cellcontextmenu" : true,
55391         /**
55392          * @event headercontextmenu
55393          * Fires when a header is right clicked
55394          * @param {Grid} this
55395          * @param {Number} columnIndex
55396          * @param {Roo.EventObject} e
55397          */
55398         "headercontextmenu" : true,
55399         /**
55400          * @event bodyscroll
55401          * Fires when the body element is scrolled
55402          * @param {Number} scrollLeft
55403          * @param {Number} scrollTop
55404          */
55405         "bodyscroll" : true,
55406         /**
55407          * @event columnresize
55408          * Fires when the user resizes a column
55409          * @param {Number} columnIndex
55410          * @param {Number} newSize
55411          */
55412         "columnresize" : true,
55413         /**
55414          * @event columnmove
55415          * Fires when the user moves a column
55416          * @param {Number} oldIndex
55417          * @param {Number} newIndex
55418          */
55419         "columnmove" : true,
55420         /**
55421          * @event startdrag
55422          * Fires when row(s) start being dragged
55423          * @param {Grid} this
55424          * @param {Roo.GridDD} dd The drag drop object
55425          * @param {event} e The raw browser event
55426          */
55427         "startdrag" : true,
55428         /**
55429          * @event enddrag
55430          * Fires when a drag operation is complete
55431          * @param {Grid} this
55432          * @param {Roo.GridDD} dd The drag drop object
55433          * @param {event} e The raw browser event
55434          */
55435         "enddrag" : true,
55436         /**
55437          * @event dragdrop
55438          * Fires when dragged row(s) are dropped on a valid DD target
55439          * @param {Grid} this
55440          * @param {Roo.GridDD} dd The drag drop object
55441          * @param {String} targetId The target drag drop object
55442          * @param {event} e The raw browser event
55443          */
55444         "dragdrop" : true,
55445         /**
55446          * @event dragover
55447          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55448          * @param {Grid} this
55449          * @param {Roo.GridDD} dd The drag drop object
55450          * @param {String} targetId The target drag drop object
55451          * @param {event} e The raw browser event
55452          */
55453         "dragover" : true,
55454         /**
55455          * @event dragenter
55456          *  Fires when the dragged row(s) first cross another DD target while being dragged
55457          * @param {Grid} this
55458          * @param {Roo.GridDD} dd The drag drop object
55459          * @param {String} targetId The target drag drop object
55460          * @param {event} e The raw browser event
55461          */
55462         "dragenter" : true,
55463         /**
55464          * @event dragout
55465          * Fires when the dragged row(s) leave another DD target while being dragged
55466          * @param {Grid} this
55467          * @param {Roo.GridDD} dd The drag drop object
55468          * @param {String} targetId The target drag drop object
55469          * @param {event} e The raw browser event
55470          */
55471         "dragout" : true,
55472         /**
55473          * @event rowclass
55474          * Fires when a row is rendered, so you can change add a style to it.
55475          * @param {GridView} gridview   The grid view
55476          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55477          */
55478         'rowclass' : true,
55479
55480         /**
55481          * @event render
55482          * Fires when the grid is rendered
55483          * @param {Grid} grid
55484          */
55485         'render' : true
55486     });
55487
55488     Roo.grid.Grid.superclass.constructor.call(this);
55489 };
55490 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55491     
55492     /**
55493      * @cfg {String} ddGroup - drag drop group.
55494      */
55495       /**
55496      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
55497      */
55498
55499     /**
55500      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55501      */
55502     minColumnWidth : 25,
55503
55504     /**
55505      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55506      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55507      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55508      */
55509     autoSizeColumns : false,
55510
55511     /**
55512      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55513      */
55514     autoSizeHeaders : true,
55515
55516     /**
55517      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55518      */
55519     monitorWindowResize : true,
55520
55521     /**
55522      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55523      * rows measured to get a columns size. Default is 0 (all rows).
55524      */
55525     maxRowsToMeasure : 0,
55526
55527     /**
55528      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55529      */
55530     trackMouseOver : true,
55531
55532     /**
55533     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55534     */
55535       /**
55536     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
55537     */
55538     
55539     /**
55540     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55541     */
55542     enableDragDrop : false,
55543     
55544     /**
55545     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55546     */
55547     enableColumnMove : true,
55548     
55549     /**
55550     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55551     */
55552     enableColumnHide : true,
55553     
55554     /**
55555     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55556     */
55557     enableRowHeightSync : false,
55558     
55559     /**
55560     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55561     */
55562     stripeRows : true,
55563     
55564     /**
55565     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55566     */
55567     autoHeight : false,
55568
55569     /**
55570      * @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.
55571      */
55572     autoExpandColumn : false,
55573
55574     /**
55575     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55576     * Default is 50.
55577     */
55578     autoExpandMin : 50,
55579
55580     /**
55581     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55582     */
55583     autoExpandMax : 1000,
55584
55585     /**
55586     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55587     */
55588     view : null,
55589
55590     /**
55591     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55592     */
55593     loadMask : false,
55594     /**
55595     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55596     */
55597     dropTarget: false,
55598     
55599    
55600     
55601     // private
55602     rendered : false,
55603
55604     /**
55605     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55606     * of a fixed width. Default is false.
55607     */
55608     /**
55609     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55610     */
55611     
55612     
55613     /**
55614     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55615     * %0 is replaced with the number of selected rows.
55616     */
55617     ddText : "{0} selected row{1}",
55618     
55619     
55620     /**
55621      * Called once after all setup has been completed and the grid is ready to be rendered.
55622      * @return {Roo.grid.Grid} this
55623      */
55624     render : function()
55625     {
55626         var c = this.container;
55627         // try to detect autoHeight/width mode
55628         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55629             this.autoHeight = true;
55630         }
55631         var view = this.getView();
55632         view.init(this);
55633
55634         c.on("click", this.onClick, this);
55635         c.on("dblclick", this.onDblClick, this);
55636         c.on("contextmenu", this.onContextMenu, this);
55637         c.on("keydown", this.onKeyDown, this);
55638         if (Roo.isTouch) {
55639             c.on("touchstart", this.onTouchStart, this);
55640         }
55641
55642         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55643
55644         this.getSelectionModel().init(this);
55645
55646         view.render();
55647
55648         if(this.loadMask){
55649             this.loadMask = new Roo.LoadMask(this.container,
55650                     Roo.apply({store:this.dataSource}, this.loadMask));
55651         }
55652         
55653         
55654         if (this.toolbar && this.toolbar.xtype) {
55655             this.toolbar.container = this.getView().getHeaderPanel(true);
55656             this.toolbar = new Roo.Toolbar(this.toolbar);
55657         }
55658         if (this.footer && this.footer.xtype) {
55659             this.footer.dataSource = this.getDataSource();
55660             this.footer.container = this.getView().getFooterPanel(true);
55661             this.footer = Roo.factory(this.footer, Roo);
55662         }
55663         if (this.dropTarget && this.dropTarget.xtype) {
55664             delete this.dropTarget.xtype;
55665             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55666         }
55667         
55668         
55669         this.rendered = true;
55670         this.fireEvent('render', this);
55671         return this;
55672     },
55673
55674     /**
55675      * Reconfigures the grid to use a different Store and Column Model.
55676      * The View will be bound to the new objects and refreshed.
55677      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55678      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55679      */
55680     reconfigure : function(dataSource, colModel){
55681         if(this.loadMask){
55682             this.loadMask.destroy();
55683             this.loadMask = new Roo.LoadMask(this.container,
55684                     Roo.apply({store:dataSource}, this.loadMask));
55685         }
55686         this.view.bind(dataSource, colModel);
55687         this.dataSource = dataSource;
55688         this.colModel = colModel;
55689         this.view.refresh(true);
55690     },
55691     /**
55692      * addColumns
55693      * Add's a column, default at the end..
55694      
55695      * @param {int} position to add (default end)
55696      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
55697      */
55698     addColumns : function(pos, ar)
55699     {
55700         
55701         for (var i =0;i< ar.length;i++) {
55702             var cfg = ar[i];
55703             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
55704             this.cm.lookup[cfg.id] = cfg;
55705         }
55706         
55707         
55708         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
55709             pos = this.cm.config.length; //this.cm.config.push(cfg);
55710         } 
55711         pos = Math.max(0,pos);
55712         ar.unshift(0);
55713         ar.unshift(pos);
55714         this.cm.config.splice.apply(this.cm.config, ar);
55715         
55716         
55717         
55718         this.view.generateRules(this.cm);
55719         this.view.refresh(true);
55720         
55721     },
55722     
55723     
55724     
55725     
55726     // private
55727     onKeyDown : function(e){
55728         this.fireEvent("keydown", e);
55729     },
55730
55731     /**
55732      * Destroy this grid.
55733      * @param {Boolean} removeEl True to remove the element
55734      */
55735     destroy : function(removeEl, keepListeners){
55736         if(this.loadMask){
55737             this.loadMask.destroy();
55738         }
55739         var c = this.container;
55740         c.removeAllListeners();
55741         this.view.destroy();
55742         this.colModel.purgeListeners();
55743         if(!keepListeners){
55744             this.purgeListeners();
55745         }
55746         c.update("");
55747         if(removeEl === true){
55748             c.remove();
55749         }
55750     },
55751
55752     // private
55753     processEvent : function(name, e){
55754         // does this fire select???
55755         //Roo.log('grid:processEvent '  + name);
55756         
55757         if (name != 'touchstart' ) {
55758             this.fireEvent(name, e);    
55759         }
55760         
55761         var t = e.getTarget();
55762         var v = this.view;
55763         var header = v.findHeaderIndex(t);
55764         if(header !== false){
55765             var ename = name == 'touchstart' ? 'click' : name;
55766              
55767             this.fireEvent("header" + ename, this, header, e);
55768         }else{
55769             var row = v.findRowIndex(t);
55770             var cell = v.findCellIndex(t);
55771             if (name == 'touchstart') {
55772                 // first touch is always a click.
55773                 // hopefull this happens after selection is updated.?
55774                 name = false;
55775                 
55776                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55777                     var cs = this.selModel.getSelectedCell();
55778                     if (row == cs[0] && cell == cs[1]){
55779                         name = 'dblclick';
55780                     }
55781                 }
55782                 if (typeof(this.selModel.getSelections) != 'undefined') {
55783                     var cs = this.selModel.getSelections();
55784                     var ds = this.dataSource;
55785                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55786                         name = 'dblclick';
55787                     }
55788                 }
55789                 if (!name) {
55790                     return;
55791                 }
55792             }
55793             
55794             
55795             if(row !== false){
55796                 this.fireEvent("row" + name, this, row, e);
55797                 if(cell !== false){
55798                     this.fireEvent("cell" + name, this, row, cell, e);
55799                 }
55800             }
55801         }
55802     },
55803
55804     // private
55805     onClick : function(e){
55806         this.processEvent("click", e);
55807     },
55808    // private
55809     onTouchStart : function(e){
55810         this.processEvent("touchstart", e);
55811     },
55812
55813     // private
55814     onContextMenu : function(e, t){
55815         this.processEvent("contextmenu", e);
55816     },
55817
55818     // private
55819     onDblClick : function(e){
55820         this.processEvent("dblclick", e);
55821     },
55822
55823     // private
55824     walkCells : function(row, col, step, fn, scope){
55825         var cm = this.colModel, clen = cm.getColumnCount();
55826         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55827         if(step < 0){
55828             if(col < 0){
55829                 row--;
55830                 first = false;
55831             }
55832             while(row >= 0){
55833                 if(!first){
55834                     col = clen-1;
55835                 }
55836                 first = false;
55837                 while(col >= 0){
55838                     if(fn.call(scope || this, row, col, cm) === true){
55839                         return [row, col];
55840                     }
55841                     col--;
55842                 }
55843                 row--;
55844             }
55845         } else {
55846             if(col >= clen){
55847                 row++;
55848                 first = false;
55849             }
55850             while(row < rlen){
55851                 if(!first){
55852                     col = 0;
55853                 }
55854                 first = false;
55855                 while(col < clen){
55856                     if(fn.call(scope || this, row, col, cm) === true){
55857                         return [row, col];
55858                     }
55859                     col++;
55860                 }
55861                 row++;
55862             }
55863         }
55864         return null;
55865     },
55866
55867     // private
55868     getSelections : function(){
55869         return this.selModel.getSelections();
55870     },
55871
55872     /**
55873      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55874      * but if manual update is required this method will initiate it.
55875      */
55876     autoSize : function(){
55877         if(this.rendered){
55878             this.view.layout();
55879             if(this.view.adjustForScroll){
55880                 this.view.adjustForScroll();
55881             }
55882         }
55883     },
55884
55885     /**
55886      * Returns the grid's underlying element.
55887      * @return {Element} The element
55888      */
55889     getGridEl : function(){
55890         return this.container;
55891     },
55892
55893     // private for compatibility, overridden by editor grid
55894     stopEditing : function(){},
55895
55896     /**
55897      * Returns the grid's SelectionModel.
55898      * @return {SelectionModel}
55899      */
55900     getSelectionModel : function(){
55901         if(!this.selModel){
55902             this.selModel = new Roo.grid.RowSelectionModel();
55903         }
55904         return this.selModel;
55905     },
55906
55907     /**
55908      * Returns the grid's DataSource.
55909      * @return {DataSource}
55910      */
55911     getDataSource : function(){
55912         return this.dataSource;
55913     },
55914
55915     /**
55916      * Returns the grid's ColumnModel.
55917      * @return {ColumnModel}
55918      */
55919     getColumnModel : function(){
55920         return this.colModel;
55921     },
55922
55923     /**
55924      * Returns the grid's GridView object.
55925      * @return {GridView}
55926      */
55927     getView : function(){
55928         if(!this.view){
55929             this.view = new Roo.grid.GridView(this.viewConfig);
55930             this.relayEvents(this.view, [
55931                 "beforerowremoved", "beforerowsinserted",
55932                 "beforerefresh", "rowremoved",
55933                 "rowsinserted", "rowupdated" ,"refresh"
55934             ]);
55935         }
55936         return this.view;
55937     },
55938     /**
55939      * Called to get grid's drag proxy text, by default returns this.ddText.
55940      * Override this to put something different in the dragged text.
55941      * @return {String}
55942      */
55943     getDragDropText : function(){
55944         var count = this.selModel.getCount();
55945         return String.format(this.ddText, count, count == 1 ? '' : 's');
55946     }
55947 });
55948 /*
55949  * Based on:
55950  * Ext JS Library 1.1.1
55951  * Copyright(c) 2006-2007, Ext JS, LLC.
55952  *
55953  * Originally Released Under LGPL - original licence link has changed is not relivant.
55954  *
55955  * Fork - LGPL
55956  * <script type="text/javascript">
55957  */
55958  
55959 Roo.grid.AbstractGridView = function(){
55960         this.grid = null;
55961         
55962         this.events = {
55963             "beforerowremoved" : true,
55964             "beforerowsinserted" : true,
55965             "beforerefresh" : true,
55966             "rowremoved" : true,
55967             "rowsinserted" : true,
55968             "rowupdated" : true,
55969             "refresh" : true
55970         };
55971     Roo.grid.AbstractGridView.superclass.constructor.call(this);
55972 };
55973
55974 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
55975     rowClass : "x-grid-row",
55976     cellClass : "x-grid-cell",
55977     tdClass : "x-grid-td",
55978     hdClass : "x-grid-hd",
55979     splitClass : "x-grid-hd-split",
55980     
55981     init: function(grid){
55982         this.grid = grid;
55983                 var cid = this.grid.getGridEl().id;
55984         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
55985         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
55986         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
55987         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
55988         },
55989         
55990     getColumnRenderers : function(){
55991         var renderers = [];
55992         var cm = this.grid.colModel;
55993         var colCount = cm.getColumnCount();
55994         for(var i = 0; i < colCount; i++){
55995             renderers[i] = cm.getRenderer(i);
55996         }
55997         return renderers;
55998     },
55999     
56000     getColumnIds : function(){
56001         var ids = [];
56002         var cm = this.grid.colModel;
56003         var colCount = cm.getColumnCount();
56004         for(var i = 0; i < colCount; i++){
56005             ids[i] = cm.getColumnId(i);
56006         }
56007         return ids;
56008     },
56009     
56010     getDataIndexes : function(){
56011         if(!this.indexMap){
56012             this.indexMap = this.buildIndexMap();
56013         }
56014         return this.indexMap.colToData;
56015     },
56016     
56017     getColumnIndexByDataIndex : function(dataIndex){
56018         if(!this.indexMap){
56019             this.indexMap = this.buildIndexMap();
56020         }
56021         return this.indexMap.dataToCol[dataIndex];
56022     },
56023     
56024     /**
56025      * Set a css style for a column dynamically. 
56026      * @param {Number} colIndex The index of the column
56027      * @param {String} name The css property name
56028      * @param {String} value The css value
56029      */
56030     setCSSStyle : function(colIndex, name, value){
56031         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
56032         Roo.util.CSS.updateRule(selector, name, value);
56033     },
56034     
56035     generateRules : function(cm){
56036         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
56037         Roo.util.CSS.removeStyleSheet(rulesId);
56038         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56039             var cid = cm.getColumnId(i);
56040             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
56041                          this.tdSelector, cid, " {\n}\n",
56042                          this.hdSelector, cid, " {\n}\n",
56043                          this.splitSelector, cid, " {\n}\n");
56044         }
56045         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56046     }
56047 });/*
56048  * Based on:
56049  * Ext JS Library 1.1.1
56050  * Copyright(c) 2006-2007, Ext JS, LLC.
56051  *
56052  * Originally Released Under LGPL - original licence link has changed is not relivant.
56053  *
56054  * Fork - LGPL
56055  * <script type="text/javascript">
56056  */
56057
56058 // private
56059 // This is a support class used internally by the Grid components
56060 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
56061     this.grid = grid;
56062     this.view = grid.getView();
56063     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56064     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
56065     if(hd2){
56066         this.setHandleElId(Roo.id(hd));
56067         this.setOuterHandleElId(Roo.id(hd2));
56068     }
56069     this.scroll = false;
56070 };
56071 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
56072     maxDragWidth: 120,
56073     getDragData : function(e){
56074         var t = Roo.lib.Event.getTarget(e);
56075         var h = this.view.findHeaderCell(t);
56076         if(h){
56077             return {ddel: h.firstChild, header:h};
56078         }
56079         return false;
56080     },
56081
56082     onInitDrag : function(e){
56083         this.view.headersDisabled = true;
56084         var clone = this.dragData.ddel.cloneNode(true);
56085         clone.id = Roo.id();
56086         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
56087         this.proxy.update(clone);
56088         return true;
56089     },
56090
56091     afterValidDrop : function(){
56092         var v = this.view;
56093         setTimeout(function(){
56094             v.headersDisabled = false;
56095         }, 50);
56096     },
56097
56098     afterInvalidDrop : function(){
56099         var v = this.view;
56100         setTimeout(function(){
56101             v.headersDisabled = false;
56102         }, 50);
56103     }
56104 });
56105 /*
56106  * Based on:
56107  * Ext JS Library 1.1.1
56108  * Copyright(c) 2006-2007, Ext JS, LLC.
56109  *
56110  * Originally Released Under LGPL - original licence link has changed is not relivant.
56111  *
56112  * Fork - LGPL
56113  * <script type="text/javascript">
56114  */
56115 // private
56116 // This is a support class used internally by the Grid components
56117 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
56118     this.grid = grid;
56119     this.view = grid.getView();
56120     // split the proxies so they don't interfere with mouse events
56121     this.proxyTop = Roo.DomHelper.append(document.body, {
56122         cls:"col-move-top", html:"&#160;"
56123     }, true);
56124     this.proxyBottom = Roo.DomHelper.append(document.body, {
56125         cls:"col-move-bottom", html:"&#160;"
56126     }, true);
56127     this.proxyTop.hide = this.proxyBottom.hide = function(){
56128         this.setLeftTop(-100,-100);
56129         this.setStyle("visibility", "hidden");
56130     };
56131     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56132     // temporarily disabled
56133     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
56134     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
56135 };
56136 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
56137     proxyOffsets : [-4, -9],
56138     fly: Roo.Element.fly,
56139
56140     getTargetFromEvent : function(e){
56141         var t = Roo.lib.Event.getTarget(e);
56142         var cindex = this.view.findCellIndex(t);
56143         if(cindex !== false){
56144             return this.view.getHeaderCell(cindex);
56145         }
56146         return null;
56147     },
56148
56149     nextVisible : function(h){
56150         var v = this.view, cm = this.grid.colModel;
56151         h = h.nextSibling;
56152         while(h){
56153             if(!cm.isHidden(v.getCellIndex(h))){
56154                 return h;
56155             }
56156             h = h.nextSibling;
56157         }
56158         return null;
56159     },
56160
56161     prevVisible : function(h){
56162         var v = this.view, cm = this.grid.colModel;
56163         h = h.prevSibling;
56164         while(h){
56165             if(!cm.isHidden(v.getCellIndex(h))){
56166                 return h;
56167             }
56168             h = h.prevSibling;
56169         }
56170         return null;
56171     },
56172
56173     positionIndicator : function(h, n, e){
56174         var x = Roo.lib.Event.getPageX(e);
56175         var r = Roo.lib.Dom.getRegion(n.firstChild);
56176         var px, pt, py = r.top + this.proxyOffsets[1];
56177         if((r.right - x) <= (r.right-r.left)/2){
56178             px = r.right+this.view.borderWidth;
56179             pt = "after";
56180         }else{
56181             px = r.left;
56182             pt = "before";
56183         }
56184         var oldIndex = this.view.getCellIndex(h);
56185         var newIndex = this.view.getCellIndex(n);
56186
56187         if(this.grid.colModel.isFixed(newIndex)){
56188             return false;
56189         }
56190
56191         var locked = this.grid.colModel.isLocked(newIndex);
56192
56193         if(pt == "after"){
56194             newIndex++;
56195         }
56196         if(oldIndex < newIndex){
56197             newIndex--;
56198         }
56199         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
56200             return false;
56201         }
56202         px +=  this.proxyOffsets[0];
56203         this.proxyTop.setLeftTop(px, py);
56204         this.proxyTop.show();
56205         if(!this.bottomOffset){
56206             this.bottomOffset = this.view.mainHd.getHeight();
56207         }
56208         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
56209         this.proxyBottom.show();
56210         return pt;
56211     },
56212
56213     onNodeEnter : function(n, dd, e, data){
56214         if(data.header != n){
56215             this.positionIndicator(data.header, n, e);
56216         }
56217     },
56218
56219     onNodeOver : function(n, dd, e, data){
56220         var result = false;
56221         if(data.header != n){
56222             result = this.positionIndicator(data.header, n, e);
56223         }
56224         if(!result){
56225             this.proxyTop.hide();
56226             this.proxyBottom.hide();
56227         }
56228         return result ? this.dropAllowed : this.dropNotAllowed;
56229     },
56230
56231     onNodeOut : function(n, dd, e, data){
56232         this.proxyTop.hide();
56233         this.proxyBottom.hide();
56234     },
56235
56236     onNodeDrop : function(n, dd, e, data){
56237         var h = data.header;
56238         if(h != n){
56239             var cm = this.grid.colModel;
56240             var x = Roo.lib.Event.getPageX(e);
56241             var r = Roo.lib.Dom.getRegion(n.firstChild);
56242             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
56243             var oldIndex = this.view.getCellIndex(h);
56244             var newIndex = this.view.getCellIndex(n);
56245             var locked = cm.isLocked(newIndex);
56246             if(pt == "after"){
56247                 newIndex++;
56248             }
56249             if(oldIndex < newIndex){
56250                 newIndex--;
56251             }
56252             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
56253                 return false;
56254             }
56255             cm.setLocked(oldIndex, locked, true);
56256             cm.moveColumn(oldIndex, newIndex);
56257             this.grid.fireEvent("columnmove", oldIndex, newIndex);
56258             return true;
56259         }
56260         return false;
56261     }
56262 });
56263 /*
56264  * Based on:
56265  * Ext JS Library 1.1.1
56266  * Copyright(c) 2006-2007, Ext JS, LLC.
56267  *
56268  * Originally Released Under LGPL - original licence link has changed is not relivant.
56269  *
56270  * Fork - LGPL
56271  * <script type="text/javascript">
56272  */
56273   
56274 /**
56275  * @class Roo.grid.GridView
56276  * @extends Roo.util.Observable
56277  *
56278  * @constructor
56279  * @param {Object} config
56280  */
56281 Roo.grid.GridView = function(config){
56282     Roo.grid.GridView.superclass.constructor.call(this);
56283     this.el = null;
56284
56285     Roo.apply(this, config);
56286 };
56287
56288 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
56289
56290     unselectable :  'unselectable="on"',
56291     unselectableCls :  'x-unselectable',
56292     
56293     
56294     rowClass : "x-grid-row",
56295
56296     cellClass : "x-grid-col",
56297
56298     tdClass : "x-grid-td",
56299
56300     hdClass : "x-grid-hd",
56301
56302     splitClass : "x-grid-split",
56303
56304     sortClasses : ["sort-asc", "sort-desc"],
56305
56306     enableMoveAnim : false,
56307
56308     hlColor: "C3DAF9",
56309
56310     dh : Roo.DomHelper,
56311
56312     fly : Roo.Element.fly,
56313
56314     css : Roo.util.CSS,
56315
56316     borderWidth: 1,
56317
56318     splitOffset: 3,
56319
56320     scrollIncrement : 22,
56321
56322     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
56323
56324     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
56325
56326     bind : function(ds, cm){
56327         if(this.ds){
56328             this.ds.un("load", this.onLoad, this);
56329             this.ds.un("datachanged", this.onDataChange, this);
56330             this.ds.un("add", this.onAdd, this);
56331             this.ds.un("remove", this.onRemove, this);
56332             this.ds.un("update", this.onUpdate, this);
56333             this.ds.un("clear", this.onClear, this);
56334         }
56335         if(ds){
56336             ds.on("load", this.onLoad, this);
56337             ds.on("datachanged", this.onDataChange, this);
56338             ds.on("add", this.onAdd, this);
56339             ds.on("remove", this.onRemove, this);
56340             ds.on("update", this.onUpdate, this);
56341             ds.on("clear", this.onClear, this);
56342         }
56343         this.ds = ds;
56344
56345         if(this.cm){
56346             this.cm.un("widthchange", this.onColWidthChange, this);
56347             this.cm.un("headerchange", this.onHeaderChange, this);
56348             this.cm.un("hiddenchange", this.onHiddenChange, this);
56349             this.cm.un("columnmoved", this.onColumnMove, this);
56350             this.cm.un("columnlockchange", this.onColumnLock, this);
56351         }
56352         if(cm){
56353             this.generateRules(cm);
56354             cm.on("widthchange", this.onColWidthChange, this);
56355             cm.on("headerchange", this.onHeaderChange, this);
56356             cm.on("hiddenchange", this.onHiddenChange, this);
56357             cm.on("columnmoved", this.onColumnMove, this);
56358             cm.on("columnlockchange", this.onColumnLock, this);
56359         }
56360         this.cm = cm;
56361     },
56362
56363     init: function(grid){
56364         Roo.grid.GridView.superclass.init.call(this, grid);
56365
56366         this.bind(grid.dataSource, grid.colModel);
56367
56368         grid.on("headerclick", this.handleHeaderClick, this);
56369
56370         if(grid.trackMouseOver){
56371             grid.on("mouseover", this.onRowOver, this);
56372             grid.on("mouseout", this.onRowOut, this);
56373         }
56374         grid.cancelTextSelection = function(){};
56375         this.gridId = grid.id;
56376
56377         var tpls = this.templates || {};
56378
56379         if(!tpls.master){
56380             tpls.master = new Roo.Template(
56381                '<div class="x-grid" hidefocus="true">',
56382                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
56383                   '<div class="x-grid-topbar"></div>',
56384                   '<div class="x-grid-scroller"><div></div></div>',
56385                   '<div class="x-grid-locked">',
56386                       '<div class="x-grid-header">{lockedHeader}</div>',
56387                       '<div class="x-grid-body">{lockedBody}</div>',
56388                   "</div>",
56389                   '<div class="x-grid-viewport">',
56390                       '<div class="x-grid-header">{header}</div>',
56391                       '<div class="x-grid-body">{body}</div>',
56392                   "</div>",
56393                   '<div class="x-grid-bottombar"></div>',
56394                  
56395                   '<div class="x-grid-resize-proxy">&#160;</div>',
56396                "</div>"
56397             );
56398             tpls.master.disableformats = true;
56399         }
56400
56401         if(!tpls.header){
56402             tpls.header = new Roo.Template(
56403                '<table border="0" cellspacing="0" cellpadding="0">',
56404                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56405                "</table>{splits}"
56406             );
56407             tpls.header.disableformats = true;
56408         }
56409         tpls.header.compile();
56410
56411         if(!tpls.hcell){
56412             tpls.hcell = new Roo.Template(
56413                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56414                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56415                 "</div></td>"
56416              );
56417              tpls.hcell.disableFormats = true;
56418         }
56419         tpls.hcell.compile();
56420
56421         if(!tpls.hsplit){
56422             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56423                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56424             tpls.hsplit.disableFormats = true;
56425         }
56426         tpls.hsplit.compile();
56427
56428         if(!tpls.body){
56429             tpls.body = new Roo.Template(
56430                '<table border="0" cellspacing="0" cellpadding="0">',
56431                "<tbody>{rows}</tbody>",
56432                "</table>"
56433             );
56434             tpls.body.disableFormats = true;
56435         }
56436         tpls.body.compile();
56437
56438         if(!tpls.row){
56439             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56440             tpls.row.disableFormats = true;
56441         }
56442         tpls.row.compile();
56443
56444         if(!tpls.cell){
56445             tpls.cell = new Roo.Template(
56446                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56447                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56448                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56449                 "</td>"
56450             );
56451             tpls.cell.disableFormats = true;
56452         }
56453         tpls.cell.compile();
56454
56455         this.templates = tpls;
56456     },
56457
56458     // remap these for backwards compat
56459     onColWidthChange : function(){
56460         this.updateColumns.apply(this, arguments);
56461     },
56462     onHeaderChange : function(){
56463         this.updateHeaders.apply(this, arguments);
56464     }, 
56465     onHiddenChange : function(){
56466         this.handleHiddenChange.apply(this, arguments);
56467     },
56468     onColumnMove : function(){
56469         this.handleColumnMove.apply(this, arguments);
56470     },
56471     onColumnLock : function(){
56472         this.handleLockChange.apply(this, arguments);
56473     },
56474
56475     onDataChange : function(){
56476         this.refresh();
56477         this.updateHeaderSortState();
56478     },
56479
56480     onClear : function(){
56481         this.refresh();
56482     },
56483
56484     onUpdate : function(ds, record){
56485         this.refreshRow(record);
56486     },
56487
56488     refreshRow : function(record){
56489         var ds = this.ds, index;
56490         if(typeof record == 'number'){
56491             index = record;
56492             record = ds.getAt(index);
56493         }else{
56494             index = ds.indexOf(record);
56495         }
56496         this.insertRows(ds, index, index, true);
56497         this.onRemove(ds, record, index+1, true);
56498         this.syncRowHeights(index, index);
56499         this.layout();
56500         this.fireEvent("rowupdated", this, index, record);
56501     },
56502
56503     onAdd : function(ds, records, index){
56504         this.insertRows(ds, index, index + (records.length-1));
56505     },
56506
56507     onRemove : function(ds, record, index, isUpdate){
56508         if(isUpdate !== true){
56509             this.fireEvent("beforerowremoved", this, index, record);
56510         }
56511         var bt = this.getBodyTable(), lt = this.getLockedTable();
56512         if(bt.rows[index]){
56513             bt.firstChild.removeChild(bt.rows[index]);
56514         }
56515         if(lt.rows[index]){
56516             lt.firstChild.removeChild(lt.rows[index]);
56517         }
56518         if(isUpdate !== true){
56519             this.stripeRows(index);
56520             this.syncRowHeights(index, index);
56521             this.layout();
56522             this.fireEvent("rowremoved", this, index, record);
56523         }
56524     },
56525
56526     onLoad : function(){
56527         this.scrollToTop();
56528     },
56529
56530     /**
56531      * Scrolls the grid to the top
56532      */
56533     scrollToTop : function(){
56534         if(this.scroller){
56535             this.scroller.dom.scrollTop = 0;
56536             this.syncScroll();
56537         }
56538     },
56539
56540     /**
56541      * Gets a panel in the header of the grid that can be used for toolbars etc.
56542      * After modifying the contents of this panel a call to grid.autoSize() may be
56543      * required to register any changes in size.
56544      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56545      * @return Roo.Element
56546      */
56547     getHeaderPanel : function(doShow){
56548         if(doShow){
56549             this.headerPanel.show();
56550         }
56551         return this.headerPanel;
56552     },
56553
56554     /**
56555      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56556      * After modifying the contents of this panel a call to grid.autoSize() may be
56557      * required to register any changes in size.
56558      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56559      * @return Roo.Element
56560      */
56561     getFooterPanel : function(doShow){
56562         if(doShow){
56563             this.footerPanel.show();
56564         }
56565         return this.footerPanel;
56566     },
56567
56568     initElements : function(){
56569         var E = Roo.Element;
56570         var el = this.grid.getGridEl().dom.firstChild;
56571         var cs = el.childNodes;
56572
56573         this.el = new E(el);
56574         
56575          this.focusEl = new E(el.firstChild);
56576         this.focusEl.swallowEvent("click", true);
56577         
56578         this.headerPanel = new E(cs[1]);
56579         this.headerPanel.enableDisplayMode("block");
56580
56581         this.scroller = new E(cs[2]);
56582         this.scrollSizer = new E(this.scroller.dom.firstChild);
56583
56584         this.lockedWrap = new E(cs[3]);
56585         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56586         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56587
56588         this.mainWrap = new E(cs[4]);
56589         this.mainHd = new E(this.mainWrap.dom.firstChild);
56590         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56591
56592         this.footerPanel = new E(cs[5]);
56593         this.footerPanel.enableDisplayMode("block");
56594
56595         this.resizeProxy = new E(cs[6]);
56596
56597         this.headerSelector = String.format(
56598            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56599            this.lockedHd.id, this.mainHd.id
56600         );
56601
56602         this.splitterSelector = String.format(
56603            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56604            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56605         );
56606     },
56607     idToCssName : function(s)
56608     {
56609         return s.replace(/[^a-z0-9]+/ig, '-');
56610     },
56611
56612     getHeaderCell : function(index){
56613         return Roo.DomQuery.select(this.headerSelector)[index];
56614     },
56615
56616     getHeaderCellMeasure : function(index){
56617         return this.getHeaderCell(index).firstChild;
56618     },
56619
56620     getHeaderCellText : function(index){
56621         return this.getHeaderCell(index).firstChild.firstChild;
56622     },
56623
56624     getLockedTable : function(){
56625         return this.lockedBody.dom.firstChild;
56626     },
56627
56628     getBodyTable : function(){
56629         return this.mainBody.dom.firstChild;
56630     },
56631
56632     getLockedRow : function(index){
56633         return this.getLockedTable().rows[index];
56634     },
56635
56636     getRow : function(index){
56637         return this.getBodyTable().rows[index];
56638     },
56639
56640     getRowComposite : function(index){
56641         if(!this.rowEl){
56642             this.rowEl = new Roo.CompositeElementLite();
56643         }
56644         var els = [], lrow, mrow;
56645         if(lrow = this.getLockedRow(index)){
56646             els.push(lrow);
56647         }
56648         if(mrow = this.getRow(index)){
56649             els.push(mrow);
56650         }
56651         this.rowEl.elements = els;
56652         return this.rowEl;
56653     },
56654     /**
56655      * Gets the 'td' of the cell
56656      * 
56657      * @param {Integer} rowIndex row to select
56658      * @param {Integer} colIndex column to select
56659      * 
56660      * @return {Object} 
56661      */
56662     getCell : function(rowIndex, colIndex){
56663         var locked = this.cm.getLockedCount();
56664         var source;
56665         if(colIndex < locked){
56666             source = this.lockedBody.dom.firstChild;
56667         }else{
56668             source = this.mainBody.dom.firstChild;
56669             colIndex -= locked;
56670         }
56671         return source.rows[rowIndex].childNodes[colIndex];
56672     },
56673
56674     getCellText : function(rowIndex, colIndex){
56675         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56676     },
56677
56678     getCellBox : function(cell){
56679         var b = this.fly(cell).getBox();
56680         if(Roo.isOpera){ // opera fails to report the Y
56681             b.y = cell.offsetTop + this.mainBody.getY();
56682         }
56683         return b;
56684     },
56685
56686     getCellIndex : function(cell){
56687         var id = String(cell.className).match(this.cellRE);
56688         if(id){
56689             return parseInt(id[1], 10);
56690         }
56691         return 0;
56692     },
56693
56694     findHeaderIndex : function(n){
56695         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56696         return r ? this.getCellIndex(r) : false;
56697     },
56698
56699     findHeaderCell : function(n){
56700         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56701         return r ? r : false;
56702     },
56703
56704     findRowIndex : function(n){
56705         if(!n){
56706             return false;
56707         }
56708         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56709         return r ? r.rowIndex : false;
56710     },
56711
56712     findCellIndex : function(node){
56713         var stop = this.el.dom;
56714         while(node && node != stop){
56715             if(this.findRE.test(node.className)){
56716                 return this.getCellIndex(node);
56717             }
56718             node = node.parentNode;
56719         }
56720         return false;
56721     },
56722
56723     getColumnId : function(index){
56724         return this.cm.getColumnId(index);
56725     },
56726
56727     getSplitters : function()
56728     {
56729         if(this.splitterSelector){
56730            return Roo.DomQuery.select(this.splitterSelector);
56731         }else{
56732             return null;
56733       }
56734     },
56735
56736     getSplitter : function(index){
56737         return this.getSplitters()[index];
56738     },
56739
56740     onRowOver : function(e, t){
56741         var row;
56742         if((row = this.findRowIndex(t)) !== false){
56743             this.getRowComposite(row).addClass("x-grid-row-over");
56744         }
56745     },
56746
56747     onRowOut : function(e, t){
56748         var row;
56749         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56750             this.getRowComposite(row).removeClass("x-grid-row-over");
56751         }
56752     },
56753
56754     renderHeaders : function(){
56755         var cm = this.cm;
56756         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56757         var cb = [], lb = [], sb = [], lsb = [], p = {};
56758         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56759             p.cellId = "x-grid-hd-0-" + i;
56760             p.splitId = "x-grid-csplit-0-" + i;
56761             p.id = cm.getColumnId(i);
56762             p.value = cm.getColumnHeader(i) || "";
56763             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56764             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56765             if(!cm.isLocked(i)){
56766                 cb[cb.length] = ct.apply(p);
56767                 sb[sb.length] = st.apply(p);
56768             }else{
56769                 lb[lb.length] = ct.apply(p);
56770                 lsb[lsb.length] = st.apply(p);
56771             }
56772         }
56773         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56774                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56775     },
56776
56777     updateHeaders : function(){
56778         var html = this.renderHeaders();
56779         this.lockedHd.update(html[0]);
56780         this.mainHd.update(html[1]);
56781     },
56782
56783     /**
56784      * Focuses the specified row.
56785      * @param {Number} row The row index
56786      */
56787     focusRow : function(row)
56788     {
56789         //Roo.log('GridView.focusRow');
56790         var x = this.scroller.dom.scrollLeft;
56791         this.focusCell(row, 0, false);
56792         this.scroller.dom.scrollLeft = x;
56793     },
56794
56795     /**
56796      * Focuses the specified cell.
56797      * @param {Number} row The row index
56798      * @param {Number} col The column index
56799      * @param {Boolean} hscroll false to disable horizontal scrolling
56800      */
56801     focusCell : function(row, col, hscroll)
56802     {
56803         //Roo.log('GridView.focusCell');
56804         var el = this.ensureVisible(row, col, hscroll);
56805         this.focusEl.alignTo(el, "tl-tl");
56806         if(Roo.isGecko){
56807             this.focusEl.focus();
56808         }else{
56809             this.focusEl.focus.defer(1, this.focusEl);
56810         }
56811     },
56812
56813     /**
56814      * Scrolls the specified cell into view
56815      * @param {Number} row The row index
56816      * @param {Number} col The column index
56817      * @param {Boolean} hscroll false to disable horizontal scrolling
56818      */
56819     ensureVisible : function(row, col, hscroll)
56820     {
56821         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56822         //return null; //disable for testing.
56823         if(typeof row != "number"){
56824             row = row.rowIndex;
56825         }
56826         if(row < 0 && row >= this.ds.getCount()){
56827             return  null;
56828         }
56829         col = (col !== undefined ? col : 0);
56830         var cm = this.grid.colModel;
56831         while(cm.isHidden(col)){
56832             col++;
56833         }
56834
56835         var el = this.getCell(row, col);
56836         if(!el){
56837             return null;
56838         }
56839         var c = this.scroller.dom;
56840
56841         var ctop = parseInt(el.offsetTop, 10);
56842         var cleft = parseInt(el.offsetLeft, 10);
56843         var cbot = ctop + el.offsetHeight;
56844         var cright = cleft + el.offsetWidth;
56845         
56846         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56847         var stop = parseInt(c.scrollTop, 10);
56848         var sleft = parseInt(c.scrollLeft, 10);
56849         var sbot = stop + ch;
56850         var sright = sleft + c.clientWidth;
56851         /*
56852         Roo.log('GridView.ensureVisible:' +
56853                 ' ctop:' + ctop +
56854                 ' c.clientHeight:' + c.clientHeight +
56855                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56856                 ' stop:' + stop +
56857                 ' cbot:' + cbot +
56858                 ' sbot:' + sbot +
56859                 ' ch:' + ch  
56860                 );
56861         */
56862         if(ctop < stop){
56863             c.scrollTop = ctop;
56864             //Roo.log("set scrolltop to ctop DISABLE?");
56865         }else if(cbot > sbot){
56866             //Roo.log("set scrolltop to cbot-ch");
56867             c.scrollTop = cbot-ch;
56868         }
56869         
56870         if(hscroll !== false){
56871             if(cleft < sleft){
56872                 c.scrollLeft = cleft;
56873             }else if(cright > sright){
56874                 c.scrollLeft = cright-c.clientWidth;
56875             }
56876         }
56877          
56878         return el;
56879     },
56880
56881     updateColumns : function(){
56882         this.grid.stopEditing();
56883         var cm = this.grid.colModel, colIds = this.getColumnIds();
56884         //var totalWidth = cm.getTotalWidth();
56885         var pos = 0;
56886         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56887             //if(cm.isHidden(i)) continue;
56888             var w = cm.getColumnWidth(i);
56889             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56890             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56891         }
56892         this.updateSplitters();
56893     },
56894
56895     generateRules : function(cm){
56896         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56897         Roo.util.CSS.removeStyleSheet(rulesId);
56898         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56899             var cid = cm.getColumnId(i);
56900             var align = '';
56901             if(cm.config[i].align){
56902                 align = 'text-align:'+cm.config[i].align+';';
56903             }
56904             var hidden = '';
56905             if(cm.isHidden(i)){
56906                 hidden = 'display:none;';
56907             }
56908             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56909             ruleBuf.push(
56910                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56911                     this.hdSelector, cid, " {\n", align, width, "}\n",
56912                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56913                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56914         }
56915         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56916     },
56917
56918     updateSplitters : function(){
56919         var cm = this.cm, s = this.getSplitters();
56920         if(s){ // splitters not created yet
56921             var pos = 0, locked = true;
56922             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56923                 if(cm.isHidden(i)) {
56924                     continue;
56925                 }
56926                 var w = cm.getColumnWidth(i); // make sure it's a number
56927                 if(!cm.isLocked(i) && locked){
56928                     pos = 0;
56929                     locked = false;
56930                 }
56931                 pos += w;
56932                 s[i].style.left = (pos-this.splitOffset) + "px";
56933             }
56934         }
56935     },
56936
56937     handleHiddenChange : function(colModel, colIndex, hidden){
56938         if(hidden){
56939             this.hideColumn(colIndex);
56940         }else{
56941             this.unhideColumn(colIndex);
56942         }
56943     },
56944
56945     hideColumn : function(colIndex){
56946         var cid = this.getColumnId(colIndex);
56947         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
56948         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
56949         if(Roo.isSafari){
56950             this.updateHeaders();
56951         }
56952         this.updateSplitters();
56953         this.layout();
56954     },
56955
56956     unhideColumn : function(colIndex){
56957         var cid = this.getColumnId(colIndex);
56958         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
56959         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
56960
56961         if(Roo.isSafari){
56962             this.updateHeaders();
56963         }
56964         this.updateSplitters();
56965         this.layout();
56966     },
56967
56968     insertRows : function(dm, firstRow, lastRow, isUpdate){
56969         if(firstRow == 0 && lastRow == dm.getCount()-1){
56970             this.refresh();
56971         }else{
56972             if(!isUpdate){
56973                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
56974             }
56975             var s = this.getScrollState();
56976             var markup = this.renderRows(firstRow, lastRow);
56977             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
56978             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
56979             this.restoreScroll(s);
56980             if(!isUpdate){
56981                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
56982                 this.syncRowHeights(firstRow, lastRow);
56983                 this.stripeRows(firstRow);
56984                 this.layout();
56985             }
56986         }
56987     },
56988
56989     bufferRows : function(markup, target, index){
56990         var before = null, trows = target.rows, tbody = target.tBodies[0];
56991         if(index < trows.length){
56992             before = trows[index];
56993         }
56994         var b = document.createElement("div");
56995         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
56996         var rows = b.firstChild.rows;
56997         for(var i = 0, len = rows.length; i < len; i++){
56998             if(before){
56999                 tbody.insertBefore(rows[0], before);
57000             }else{
57001                 tbody.appendChild(rows[0]);
57002             }
57003         }
57004         b.innerHTML = "";
57005         b = null;
57006     },
57007
57008     deleteRows : function(dm, firstRow, lastRow){
57009         if(dm.getRowCount()<1){
57010             this.fireEvent("beforerefresh", this);
57011             this.mainBody.update("");
57012             this.lockedBody.update("");
57013             this.fireEvent("refresh", this);
57014         }else{
57015             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
57016             var bt = this.getBodyTable();
57017             var tbody = bt.firstChild;
57018             var rows = bt.rows;
57019             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
57020                 tbody.removeChild(rows[firstRow]);
57021             }
57022             this.stripeRows(firstRow);
57023             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
57024         }
57025     },
57026
57027     updateRows : function(dataSource, firstRow, lastRow){
57028         var s = this.getScrollState();
57029         this.refresh();
57030         this.restoreScroll(s);
57031     },
57032
57033     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
57034         if(!noRefresh){
57035            this.refresh();
57036         }
57037         this.updateHeaderSortState();
57038     },
57039
57040     getScrollState : function(){
57041         
57042         var sb = this.scroller.dom;
57043         return {left: sb.scrollLeft, top: sb.scrollTop};
57044     },
57045
57046     stripeRows : function(startRow){
57047         if(!this.grid.stripeRows || this.ds.getCount() < 1){
57048             return;
57049         }
57050         startRow = startRow || 0;
57051         var rows = this.getBodyTable().rows;
57052         var lrows = this.getLockedTable().rows;
57053         var cls = ' x-grid-row-alt ';
57054         for(var i = startRow, len = rows.length; i < len; i++){
57055             var row = rows[i], lrow = lrows[i];
57056             var isAlt = ((i+1) % 2 == 0);
57057             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
57058             if(isAlt == hasAlt){
57059                 continue;
57060             }
57061             if(isAlt){
57062                 row.className += " x-grid-row-alt";
57063             }else{
57064                 row.className = row.className.replace("x-grid-row-alt", "");
57065             }
57066             if(lrow){
57067                 lrow.className = row.className;
57068             }
57069         }
57070     },
57071
57072     restoreScroll : function(state){
57073         //Roo.log('GridView.restoreScroll');
57074         var sb = this.scroller.dom;
57075         sb.scrollLeft = state.left;
57076         sb.scrollTop = state.top;
57077         this.syncScroll();
57078     },
57079
57080     syncScroll : function(){
57081         //Roo.log('GridView.syncScroll');
57082         var sb = this.scroller.dom;
57083         var sh = this.mainHd.dom;
57084         var bs = this.mainBody.dom;
57085         var lv = this.lockedBody.dom;
57086         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
57087         lv.scrollTop = bs.scrollTop = sb.scrollTop;
57088     },
57089
57090     handleScroll : function(e){
57091         this.syncScroll();
57092         var sb = this.scroller.dom;
57093         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
57094         e.stopEvent();
57095     },
57096
57097     handleWheel : function(e){
57098         var d = e.getWheelDelta();
57099         this.scroller.dom.scrollTop -= d*22;
57100         // set this here to prevent jumpy scrolling on large tables
57101         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
57102         e.stopEvent();
57103     },
57104
57105     renderRows : function(startRow, endRow){
57106         // pull in all the crap needed to render rows
57107         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
57108         var colCount = cm.getColumnCount();
57109
57110         if(ds.getCount() < 1){
57111             return ["", ""];
57112         }
57113
57114         // build a map for all the columns
57115         var cs = [];
57116         for(var i = 0; i < colCount; i++){
57117             var name = cm.getDataIndex(i);
57118             cs[i] = {
57119                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
57120                 renderer : cm.getRenderer(i),
57121                 id : cm.getColumnId(i),
57122                 locked : cm.isLocked(i),
57123                 has_editor : cm.isCellEditable(i)
57124             };
57125         }
57126
57127         startRow = startRow || 0;
57128         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
57129
57130         // records to render
57131         var rs = ds.getRange(startRow, endRow);
57132
57133         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
57134     },
57135
57136     // As much as I hate to duplicate code, this was branched because FireFox really hates
57137     // [].join("") on strings. The performance difference was substantial enough to
57138     // branch this function
57139     doRender : Roo.isGecko ?
57140             function(cs, rs, ds, startRow, colCount, stripe){
57141                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57142                 // buffers
57143                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57144                 
57145                 var hasListener = this.grid.hasListener('rowclass');
57146                 var rowcfg = {};
57147                 for(var j = 0, len = rs.length; j < len; j++){
57148                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
57149                     for(var i = 0; i < colCount; i++){
57150                         c = cs[i];
57151                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57152                         p.id = c.id;
57153                         p.css = p.attr = "";
57154                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57155                         if(p.value == undefined || p.value === "") {
57156                             p.value = "&#160;";
57157                         }
57158                         if(c.has_editor){
57159                             p.css += ' x-grid-editable-cell';
57160                         }
57161                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
57162                             p.css +=  ' x-grid-dirty-cell';
57163                         }
57164                         var markup = ct.apply(p);
57165                         if(!c.locked){
57166                             cb+= markup;
57167                         }else{
57168                             lcb+= markup;
57169                         }
57170                     }
57171                     var alt = [];
57172                     if(stripe && ((rowIndex+1) % 2 == 0)){
57173                         alt.push("x-grid-row-alt")
57174                     }
57175                     if(r.dirty){
57176                         alt.push(  " x-grid-dirty-row");
57177                     }
57178                     rp.cells = lcb;
57179                     if(this.getRowClass){
57180                         alt.push(this.getRowClass(r, rowIndex));
57181                     }
57182                     if (hasListener) {
57183                         rowcfg = {
57184                              
57185                             record: r,
57186                             rowIndex : rowIndex,
57187                             rowClass : ''
57188                         };
57189                         this.grid.fireEvent('rowclass', this, rowcfg);
57190                         alt.push(rowcfg.rowClass);
57191                     }
57192                     rp.alt = alt.join(" ");
57193                     lbuf+= rt.apply(rp);
57194                     rp.cells = cb;
57195                     buf+=  rt.apply(rp);
57196                 }
57197                 return [lbuf, buf];
57198             } :
57199             function(cs, rs, ds, startRow, colCount, stripe){
57200                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57201                 // buffers
57202                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57203                 var hasListener = this.grid.hasListener('rowclass');
57204  
57205                 var rowcfg = {};
57206                 for(var j = 0, len = rs.length; j < len; j++){
57207                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
57208                     for(var i = 0; i < colCount; i++){
57209                         c = cs[i];
57210                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57211                         p.id = c.id;
57212                         p.css = p.attr = "";
57213                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57214                         if(p.value == undefined || p.value === "") {
57215                             p.value = "&#160;";
57216                         }
57217                         //Roo.log(c);
57218                          if(c.has_editor){
57219                             p.css += ' x-grid-editable-cell';
57220                         }
57221                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
57222                             p.css += ' x-grid-dirty-cell' 
57223                         }
57224                         
57225                         var markup = ct.apply(p);
57226                         if(!c.locked){
57227                             cb[cb.length] = markup;
57228                         }else{
57229                             lcb[lcb.length] = markup;
57230                         }
57231                     }
57232                     var alt = [];
57233                     if(stripe && ((rowIndex+1) % 2 == 0)){
57234                         alt.push( "x-grid-row-alt");
57235                     }
57236                     if(r.dirty){
57237                         alt.push(" x-grid-dirty-row");
57238                     }
57239                     rp.cells = lcb;
57240                     if(this.getRowClass){
57241                         alt.push( this.getRowClass(r, rowIndex));
57242                     }
57243                     if (hasListener) {
57244                         rowcfg = {
57245                              
57246                             record: r,
57247                             rowIndex : rowIndex,
57248                             rowClass : ''
57249                         };
57250                         this.grid.fireEvent('rowclass', this, rowcfg);
57251                         alt.push(rowcfg.rowClass);
57252                     }
57253                     
57254                     rp.alt = alt.join(" ");
57255                     rp.cells = lcb.join("");
57256                     lbuf[lbuf.length] = rt.apply(rp);
57257                     rp.cells = cb.join("");
57258                     buf[buf.length] =  rt.apply(rp);
57259                 }
57260                 return [lbuf.join(""), buf.join("")];
57261             },
57262
57263     renderBody : function(){
57264         var markup = this.renderRows();
57265         var bt = this.templates.body;
57266         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
57267     },
57268
57269     /**
57270      * Refreshes the grid
57271      * @param {Boolean} headersToo
57272      */
57273     refresh : function(headersToo){
57274         this.fireEvent("beforerefresh", this);
57275         this.grid.stopEditing();
57276         var result = this.renderBody();
57277         this.lockedBody.update(result[0]);
57278         this.mainBody.update(result[1]);
57279         if(headersToo === true){
57280             this.updateHeaders();
57281             this.updateColumns();
57282             this.updateSplitters();
57283             this.updateHeaderSortState();
57284         }
57285         this.syncRowHeights();
57286         this.layout();
57287         this.fireEvent("refresh", this);
57288     },
57289
57290     handleColumnMove : function(cm, oldIndex, newIndex){
57291         this.indexMap = null;
57292         var s = this.getScrollState();
57293         this.refresh(true);
57294         this.restoreScroll(s);
57295         this.afterMove(newIndex);
57296     },
57297
57298     afterMove : function(colIndex){
57299         if(this.enableMoveAnim && Roo.enableFx){
57300             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
57301         }
57302         // if multisort - fix sortOrder, and reload..
57303         if (this.grid.dataSource.multiSort) {
57304             // the we can call sort again..
57305             var dm = this.grid.dataSource;
57306             var cm = this.grid.colModel;
57307             var so = [];
57308             for(var i = 0; i < cm.config.length; i++ ) {
57309                 
57310                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
57311                     continue; // dont' bother, it's not in sort list or being set.
57312                 }
57313                 
57314                 so.push(cm.config[i].dataIndex);
57315             };
57316             dm.sortOrder = so;
57317             dm.load(dm.lastOptions);
57318             
57319             
57320         }
57321         
57322     },
57323
57324     updateCell : function(dm, rowIndex, dataIndex){
57325         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
57326         if(typeof colIndex == "undefined"){ // not present in grid
57327             return;
57328         }
57329         var cm = this.grid.colModel;
57330         var cell = this.getCell(rowIndex, colIndex);
57331         var cellText = this.getCellText(rowIndex, colIndex);
57332
57333         var p = {
57334             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
57335             id : cm.getColumnId(colIndex),
57336             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
57337         };
57338         var renderer = cm.getRenderer(colIndex);
57339         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
57340         if(typeof val == "undefined" || val === "") {
57341             val = "&#160;";
57342         }
57343         cellText.innerHTML = val;
57344         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
57345         this.syncRowHeights(rowIndex, rowIndex);
57346     },
57347
57348     calcColumnWidth : function(colIndex, maxRowsToMeasure){
57349         var maxWidth = 0;
57350         if(this.grid.autoSizeHeaders){
57351             var h = this.getHeaderCellMeasure(colIndex);
57352             maxWidth = Math.max(maxWidth, h.scrollWidth);
57353         }
57354         var tb, index;
57355         if(this.cm.isLocked(colIndex)){
57356             tb = this.getLockedTable();
57357             index = colIndex;
57358         }else{
57359             tb = this.getBodyTable();
57360             index = colIndex - this.cm.getLockedCount();
57361         }
57362         if(tb && tb.rows){
57363             var rows = tb.rows;
57364             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
57365             for(var i = 0; i < stopIndex; i++){
57366                 var cell = rows[i].childNodes[index].firstChild;
57367                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
57368             }
57369         }
57370         return maxWidth + /*margin for error in IE*/ 5;
57371     },
57372     /**
57373      * Autofit a column to its content.
57374      * @param {Number} colIndex
57375      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
57376      */
57377      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
57378          if(this.cm.isHidden(colIndex)){
57379              return; // can't calc a hidden column
57380          }
57381         if(forceMinSize){
57382             var cid = this.cm.getColumnId(colIndex);
57383             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
57384            if(this.grid.autoSizeHeaders){
57385                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
57386            }
57387         }
57388         var newWidth = this.calcColumnWidth(colIndex);
57389         this.cm.setColumnWidth(colIndex,
57390             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57391         if(!suppressEvent){
57392             this.grid.fireEvent("columnresize", colIndex, newWidth);
57393         }
57394     },
57395
57396     /**
57397      * Autofits all columns to their content and then expands to fit any extra space in the grid
57398      */
57399      autoSizeColumns : function(){
57400         var cm = this.grid.colModel;
57401         var colCount = cm.getColumnCount();
57402         for(var i = 0; i < colCount; i++){
57403             this.autoSizeColumn(i, true, true);
57404         }
57405         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57406             this.fitColumns();
57407         }else{
57408             this.updateColumns();
57409             this.layout();
57410         }
57411     },
57412
57413     /**
57414      * Autofits all columns to the grid's width proportionate with their current size
57415      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57416      */
57417     fitColumns : function(reserveScrollSpace){
57418         var cm = this.grid.colModel;
57419         var colCount = cm.getColumnCount();
57420         var cols = [];
57421         var width = 0;
57422         var i, w;
57423         for (i = 0; i < colCount; i++){
57424             if(!cm.isHidden(i) && !cm.isFixed(i)){
57425                 w = cm.getColumnWidth(i);
57426                 cols.push(i);
57427                 cols.push(w);
57428                 width += w;
57429             }
57430         }
57431         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57432         if(reserveScrollSpace){
57433             avail -= 17;
57434         }
57435         var frac = (avail - cm.getTotalWidth())/width;
57436         while (cols.length){
57437             w = cols.pop();
57438             i = cols.pop();
57439             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57440         }
57441         this.updateColumns();
57442         this.layout();
57443     },
57444
57445     onRowSelect : function(rowIndex){
57446         var row = this.getRowComposite(rowIndex);
57447         row.addClass("x-grid-row-selected");
57448     },
57449
57450     onRowDeselect : function(rowIndex){
57451         var row = this.getRowComposite(rowIndex);
57452         row.removeClass("x-grid-row-selected");
57453     },
57454
57455     onCellSelect : function(row, col){
57456         var cell = this.getCell(row, col);
57457         if(cell){
57458             Roo.fly(cell).addClass("x-grid-cell-selected");
57459         }
57460     },
57461
57462     onCellDeselect : function(row, col){
57463         var cell = this.getCell(row, col);
57464         if(cell){
57465             Roo.fly(cell).removeClass("x-grid-cell-selected");
57466         }
57467     },
57468
57469     updateHeaderSortState : function(){
57470         
57471         // sort state can be single { field: xxx, direction : yyy}
57472         // or   { xxx=>ASC , yyy : DESC ..... }
57473         
57474         var mstate = {};
57475         if (!this.ds.multiSort) { 
57476             var state = this.ds.getSortState();
57477             if(!state){
57478                 return;
57479             }
57480             mstate[state.field] = state.direction;
57481             // FIXME... - this is not used here.. but might be elsewhere..
57482             this.sortState = state;
57483             
57484         } else {
57485             mstate = this.ds.sortToggle;
57486         }
57487         //remove existing sort classes..
57488         
57489         var sc = this.sortClasses;
57490         var hds = this.el.select(this.headerSelector).removeClass(sc);
57491         
57492         for(var f in mstate) {
57493         
57494             var sortColumn = this.cm.findColumnIndex(f);
57495             
57496             if(sortColumn != -1){
57497                 var sortDir = mstate[f];        
57498                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57499             }
57500         }
57501         
57502          
57503         
57504     },
57505
57506
57507     handleHeaderClick : function(g, index,e){
57508         
57509         Roo.log("header click");
57510         
57511         if (Roo.isTouch) {
57512             // touch events on header are handled by context
57513             this.handleHdCtx(g,index,e);
57514             return;
57515         }
57516         
57517         
57518         if(this.headersDisabled){
57519             return;
57520         }
57521         var dm = g.dataSource, cm = g.colModel;
57522         if(!cm.isSortable(index)){
57523             return;
57524         }
57525         g.stopEditing();
57526         
57527         if (dm.multiSort) {
57528             // update the sortOrder
57529             var so = [];
57530             for(var i = 0; i < cm.config.length; i++ ) {
57531                 
57532                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57533                     continue; // dont' bother, it's not in sort list or being set.
57534                 }
57535                 
57536                 so.push(cm.config[i].dataIndex);
57537             };
57538             dm.sortOrder = so;
57539         }
57540         
57541         
57542         dm.sort(cm.getDataIndex(index));
57543     },
57544
57545
57546     destroy : function(){
57547         if(this.colMenu){
57548             this.colMenu.removeAll();
57549             Roo.menu.MenuMgr.unregister(this.colMenu);
57550             this.colMenu.getEl().remove();
57551             delete this.colMenu;
57552         }
57553         if(this.hmenu){
57554             this.hmenu.removeAll();
57555             Roo.menu.MenuMgr.unregister(this.hmenu);
57556             this.hmenu.getEl().remove();
57557             delete this.hmenu;
57558         }
57559         if(this.grid.enableColumnMove){
57560             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57561             if(dds){
57562                 for(var dd in dds){
57563                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57564                         var elid = dds[dd].dragElId;
57565                         dds[dd].unreg();
57566                         Roo.get(elid).remove();
57567                     } else if(dds[dd].config.isTarget){
57568                         dds[dd].proxyTop.remove();
57569                         dds[dd].proxyBottom.remove();
57570                         dds[dd].unreg();
57571                     }
57572                     if(Roo.dd.DDM.locationCache[dd]){
57573                         delete Roo.dd.DDM.locationCache[dd];
57574                     }
57575                 }
57576                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57577             }
57578         }
57579         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57580         this.bind(null, null);
57581         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57582     },
57583
57584     handleLockChange : function(){
57585         this.refresh(true);
57586     },
57587
57588     onDenyColumnLock : function(){
57589
57590     },
57591
57592     onDenyColumnHide : function(){
57593
57594     },
57595
57596     handleHdMenuClick : function(item){
57597         var index = this.hdCtxIndex;
57598         var cm = this.cm, ds = this.ds;
57599         switch(item.id){
57600             case "asc":
57601                 ds.sort(cm.getDataIndex(index), "ASC");
57602                 break;
57603             case "desc":
57604                 ds.sort(cm.getDataIndex(index), "DESC");
57605                 break;
57606             case "lock":
57607                 var lc = cm.getLockedCount();
57608                 if(cm.getColumnCount(true) <= lc+1){
57609                     this.onDenyColumnLock();
57610                     return;
57611                 }
57612                 if(lc != index){
57613                     cm.setLocked(index, true, true);
57614                     cm.moveColumn(index, lc);
57615                     this.grid.fireEvent("columnmove", index, lc);
57616                 }else{
57617                     cm.setLocked(index, true);
57618                 }
57619             break;
57620             case "unlock":
57621                 var lc = cm.getLockedCount();
57622                 if((lc-1) != index){
57623                     cm.setLocked(index, false, true);
57624                     cm.moveColumn(index, lc-1);
57625                     this.grid.fireEvent("columnmove", index, lc-1);
57626                 }else{
57627                     cm.setLocked(index, false);
57628                 }
57629             break;
57630             case 'wider': // used to expand cols on touch..
57631             case 'narrow':
57632                 var cw = cm.getColumnWidth(index);
57633                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57634                 cw = Math.max(0, cw);
57635                 cw = Math.min(cw,4000);
57636                 cm.setColumnWidth(index, cw);
57637                 break;
57638                 
57639             default:
57640                 index = cm.getIndexById(item.id.substr(4));
57641                 if(index != -1){
57642                     if(item.checked && cm.getColumnCount(true) <= 1){
57643                         this.onDenyColumnHide();
57644                         return false;
57645                     }
57646                     cm.setHidden(index, item.checked);
57647                 }
57648         }
57649         return true;
57650     },
57651
57652     beforeColMenuShow : function(){
57653         var cm = this.cm,  colCount = cm.getColumnCount();
57654         this.colMenu.removeAll();
57655         for(var i = 0; i < colCount; i++){
57656             this.colMenu.add(new Roo.menu.CheckItem({
57657                 id: "col-"+cm.getColumnId(i),
57658                 text: cm.getColumnHeader(i),
57659                 checked: !cm.isHidden(i),
57660                 hideOnClick:false
57661             }));
57662         }
57663     },
57664
57665     handleHdCtx : function(g, index, e){
57666         e.stopEvent();
57667         var hd = this.getHeaderCell(index);
57668         this.hdCtxIndex = index;
57669         var ms = this.hmenu.items, cm = this.cm;
57670         ms.get("asc").setDisabled(!cm.isSortable(index));
57671         ms.get("desc").setDisabled(!cm.isSortable(index));
57672         if(this.grid.enableColLock !== false){
57673             ms.get("lock").setDisabled(cm.isLocked(index));
57674             ms.get("unlock").setDisabled(!cm.isLocked(index));
57675         }
57676         this.hmenu.show(hd, "tl-bl");
57677     },
57678
57679     handleHdOver : function(e){
57680         var hd = this.findHeaderCell(e.getTarget());
57681         if(hd && !this.headersDisabled){
57682             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57683                this.fly(hd).addClass("x-grid-hd-over");
57684             }
57685         }
57686     },
57687
57688     handleHdOut : function(e){
57689         var hd = this.findHeaderCell(e.getTarget());
57690         if(hd){
57691             this.fly(hd).removeClass("x-grid-hd-over");
57692         }
57693     },
57694
57695     handleSplitDblClick : function(e, t){
57696         var i = this.getCellIndex(t);
57697         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57698             this.autoSizeColumn(i, true);
57699             this.layout();
57700         }
57701     },
57702
57703     render : function(){
57704
57705         var cm = this.cm;
57706         var colCount = cm.getColumnCount();
57707
57708         if(this.grid.monitorWindowResize === true){
57709             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57710         }
57711         var header = this.renderHeaders();
57712         var body = this.templates.body.apply({rows:""});
57713         var html = this.templates.master.apply({
57714             lockedBody: body,
57715             body: body,
57716             lockedHeader: header[0],
57717             header: header[1]
57718         });
57719
57720         //this.updateColumns();
57721
57722         this.grid.getGridEl().dom.innerHTML = html;
57723
57724         this.initElements();
57725         
57726         // a kludge to fix the random scolling effect in webkit
57727         this.el.on("scroll", function() {
57728             this.el.dom.scrollTop=0; // hopefully not recursive..
57729         },this);
57730
57731         this.scroller.on("scroll", this.handleScroll, this);
57732         this.lockedBody.on("mousewheel", this.handleWheel, this);
57733         this.mainBody.on("mousewheel", this.handleWheel, this);
57734
57735         this.mainHd.on("mouseover", this.handleHdOver, this);
57736         this.mainHd.on("mouseout", this.handleHdOut, this);
57737         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57738                 {delegate: "."+this.splitClass});
57739
57740         this.lockedHd.on("mouseover", this.handleHdOver, this);
57741         this.lockedHd.on("mouseout", this.handleHdOut, this);
57742         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57743                 {delegate: "."+this.splitClass});
57744
57745         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57746             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57747         }
57748
57749         this.updateSplitters();
57750
57751         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57752             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57753             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57754         }
57755
57756         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57757             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57758             this.hmenu.add(
57759                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57760                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57761             );
57762             if(this.grid.enableColLock !== false){
57763                 this.hmenu.add('-',
57764                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57765                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57766                 );
57767             }
57768             if (Roo.isTouch) {
57769                  this.hmenu.add('-',
57770                     {id:"wider", text: this.columnsWiderText},
57771                     {id:"narrow", text: this.columnsNarrowText }
57772                 );
57773                 
57774                  
57775             }
57776             
57777             if(this.grid.enableColumnHide !== false){
57778
57779                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57780                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57781                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57782
57783                 this.hmenu.add('-',
57784                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57785                 );
57786             }
57787             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57788
57789             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57790         }
57791
57792         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57793             this.dd = new Roo.grid.GridDragZone(this.grid, {
57794                 ddGroup : this.grid.ddGroup || 'GridDD'
57795             });
57796             
57797         }
57798
57799         /*
57800         for(var i = 0; i < colCount; i++){
57801             if(cm.isHidden(i)){
57802                 this.hideColumn(i);
57803             }
57804             if(cm.config[i].align){
57805                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57806                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57807             }
57808         }*/
57809         
57810         this.updateHeaderSortState();
57811
57812         this.beforeInitialResize();
57813         this.layout(true);
57814
57815         // two part rendering gives faster view to the user
57816         this.renderPhase2.defer(1, this);
57817     },
57818
57819     renderPhase2 : function(){
57820         // render the rows now
57821         this.refresh();
57822         if(this.grid.autoSizeColumns){
57823             this.autoSizeColumns();
57824         }
57825     },
57826
57827     beforeInitialResize : function(){
57828
57829     },
57830
57831     onColumnSplitterMoved : function(i, w){
57832         this.userResized = true;
57833         var cm = this.grid.colModel;
57834         cm.setColumnWidth(i, w, true);
57835         var cid = cm.getColumnId(i);
57836         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57837         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57838         this.updateSplitters();
57839         this.layout();
57840         this.grid.fireEvent("columnresize", i, w);
57841     },
57842
57843     syncRowHeights : function(startIndex, endIndex){
57844         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57845             startIndex = startIndex || 0;
57846             var mrows = this.getBodyTable().rows;
57847             var lrows = this.getLockedTable().rows;
57848             var len = mrows.length-1;
57849             endIndex = Math.min(endIndex || len, len);
57850             for(var i = startIndex; i <= endIndex; i++){
57851                 var m = mrows[i], l = lrows[i];
57852                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57853                 m.style.height = l.style.height = h + "px";
57854             }
57855         }
57856     },
57857
57858     layout : function(initialRender, is2ndPass)
57859     {
57860         var g = this.grid;
57861         var auto = g.autoHeight;
57862         var scrollOffset = 16;
57863         var c = g.getGridEl(), cm = this.cm,
57864                 expandCol = g.autoExpandColumn,
57865                 gv = this;
57866         //c.beginMeasure();
57867
57868         if(!c.dom.offsetWidth){ // display:none?
57869             if(initialRender){
57870                 this.lockedWrap.show();
57871                 this.mainWrap.show();
57872             }
57873             return;
57874         }
57875
57876         var hasLock = this.cm.isLocked(0);
57877
57878         var tbh = this.headerPanel.getHeight();
57879         var bbh = this.footerPanel.getHeight();
57880
57881         if(auto){
57882             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57883             var newHeight = ch + c.getBorderWidth("tb");
57884             if(g.maxHeight){
57885                 newHeight = Math.min(g.maxHeight, newHeight);
57886             }
57887             c.setHeight(newHeight);
57888         }
57889
57890         if(g.autoWidth){
57891             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57892         }
57893
57894         var s = this.scroller;
57895
57896         var csize = c.getSize(true);
57897
57898         this.el.setSize(csize.width, csize.height);
57899
57900         this.headerPanel.setWidth(csize.width);
57901         this.footerPanel.setWidth(csize.width);
57902
57903         var hdHeight = this.mainHd.getHeight();
57904         var vw = csize.width;
57905         var vh = csize.height - (tbh + bbh);
57906
57907         s.setSize(vw, vh);
57908
57909         var bt = this.getBodyTable();
57910         
57911         if(cm.getLockedCount() == cm.config.length){
57912             bt = this.getLockedTable();
57913         }
57914         
57915         var ltWidth = hasLock ?
57916                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57917
57918         var scrollHeight = bt.offsetHeight;
57919         var scrollWidth = ltWidth + bt.offsetWidth;
57920         var vscroll = false, hscroll = false;
57921
57922         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57923
57924         var lw = this.lockedWrap, mw = this.mainWrap;
57925         var lb = this.lockedBody, mb = this.mainBody;
57926
57927         setTimeout(function(){
57928             var t = s.dom.offsetTop;
57929             var w = s.dom.clientWidth,
57930                 h = s.dom.clientHeight;
57931
57932             lw.setTop(t);
57933             lw.setSize(ltWidth, h);
57934
57935             mw.setLeftTop(ltWidth, t);
57936             mw.setSize(w-ltWidth, h);
57937
57938             lb.setHeight(h-hdHeight);
57939             mb.setHeight(h-hdHeight);
57940
57941             if(is2ndPass !== true && !gv.userResized && expandCol){
57942                 // high speed resize without full column calculation
57943                 
57944                 var ci = cm.getIndexById(expandCol);
57945                 if (ci < 0) {
57946                     ci = cm.findColumnIndex(expandCol);
57947                 }
57948                 ci = Math.max(0, ci); // make sure it's got at least the first col.
57949                 var expandId = cm.getColumnId(ci);
57950                 var  tw = cm.getTotalWidth(false);
57951                 var currentWidth = cm.getColumnWidth(ci);
57952                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
57953                 if(currentWidth != cw){
57954                     cm.setColumnWidth(ci, cw, true);
57955                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57956                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57957                     gv.updateSplitters();
57958                     gv.layout(false, true);
57959                 }
57960             }
57961
57962             if(initialRender){
57963                 lw.show();
57964                 mw.show();
57965             }
57966             //c.endMeasure();
57967         }, 10);
57968     },
57969
57970     onWindowResize : function(){
57971         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
57972             return;
57973         }
57974         this.layout();
57975     },
57976
57977     appendFooter : function(parentEl){
57978         return null;
57979     },
57980
57981     sortAscText : "Sort Ascending",
57982     sortDescText : "Sort Descending",
57983     lockText : "Lock Column",
57984     unlockText : "Unlock Column",
57985     columnsText : "Columns",
57986  
57987     columnsWiderText : "Wider",
57988     columnsNarrowText : "Thinner"
57989 });
57990
57991
57992 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
57993     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
57994     this.proxy.el.addClass('x-grid3-col-dd');
57995 };
57996
57997 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
57998     handleMouseDown : function(e){
57999
58000     },
58001
58002     callHandleMouseDown : function(e){
58003         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
58004     }
58005 });
58006 /*
58007  * Based on:
58008  * Ext JS Library 1.1.1
58009  * Copyright(c) 2006-2007, Ext JS, LLC.
58010  *
58011  * Originally Released Under LGPL - original licence link has changed is not relivant.
58012  *
58013  * Fork - LGPL
58014  * <script type="text/javascript">
58015  */
58016  
58017 // private
58018 // This is a support class used internally by the Grid components
58019 Roo.grid.SplitDragZone = function(grid, hd, hd2){
58020     this.grid = grid;
58021     this.view = grid.getView();
58022     this.proxy = this.view.resizeProxy;
58023     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
58024         "gridSplitters" + this.grid.getGridEl().id, {
58025         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
58026     });
58027     this.setHandleElId(Roo.id(hd));
58028     this.setOuterHandleElId(Roo.id(hd2));
58029     this.scroll = false;
58030 };
58031 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
58032     fly: Roo.Element.fly,
58033
58034     b4StartDrag : function(x, y){
58035         this.view.headersDisabled = true;
58036         this.proxy.setHeight(this.view.mainWrap.getHeight());
58037         var w = this.cm.getColumnWidth(this.cellIndex);
58038         var minw = Math.max(w-this.grid.minColumnWidth, 0);
58039         this.resetConstraints();
58040         this.setXConstraint(minw, 1000);
58041         this.setYConstraint(0, 0);
58042         this.minX = x - minw;
58043         this.maxX = x + 1000;
58044         this.startPos = x;
58045         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
58046     },
58047
58048
58049     handleMouseDown : function(e){
58050         ev = Roo.EventObject.setEvent(e);
58051         var t = this.fly(ev.getTarget());
58052         if(t.hasClass("x-grid-split")){
58053             this.cellIndex = this.view.getCellIndex(t.dom);
58054             this.split = t.dom;
58055             this.cm = this.grid.colModel;
58056             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
58057                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
58058             }
58059         }
58060     },
58061
58062     endDrag : function(e){
58063         this.view.headersDisabled = false;
58064         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
58065         var diff = endX - this.startPos;
58066         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
58067     },
58068
58069     autoOffset : function(){
58070         this.setDelta(0,0);
58071     }
58072 });/*
58073  * Based on:
58074  * Ext JS Library 1.1.1
58075  * Copyright(c) 2006-2007, Ext JS, LLC.
58076  *
58077  * Originally Released Under LGPL - original licence link has changed is not relivant.
58078  *
58079  * Fork - LGPL
58080  * <script type="text/javascript">
58081  */
58082  
58083 // private
58084 // This is a support class used internally by the Grid components
58085 Roo.grid.GridDragZone = function(grid, config){
58086     this.view = grid.getView();
58087     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
58088     if(this.view.lockedBody){
58089         this.setHandleElId(Roo.id(this.view.mainBody.dom));
58090         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
58091     }
58092     this.scroll = false;
58093     this.grid = grid;
58094     this.ddel = document.createElement('div');
58095     this.ddel.className = 'x-grid-dd-wrap';
58096 };
58097
58098 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
58099     ddGroup : "GridDD",
58100
58101     getDragData : function(e){
58102         var t = Roo.lib.Event.getTarget(e);
58103         var rowIndex = this.view.findRowIndex(t);
58104         var sm = this.grid.selModel;
58105             
58106         //Roo.log(rowIndex);
58107         
58108         if (sm.getSelectedCell) {
58109             // cell selection..
58110             if (!sm.getSelectedCell()) {
58111                 return false;
58112             }
58113             if (rowIndex != sm.getSelectedCell()[0]) {
58114                 return false;
58115             }
58116         
58117         }
58118         if (sm.getSelections && sm.getSelections().length < 1) {
58119             return false;
58120         }
58121         
58122         
58123         // before it used to all dragging of unseleted... - now we dont do that.
58124         if(rowIndex !== false){
58125             
58126             // if editorgrid.. 
58127             
58128             
58129             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
58130                
58131             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
58132               //  
58133             //}
58134             if (e.hasModifier()){
58135                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
58136             }
58137             
58138             Roo.log("getDragData");
58139             
58140             return {
58141                 grid: this.grid,
58142                 ddel: this.ddel,
58143                 rowIndex: rowIndex,
58144                 selections: sm.getSelections ? sm.getSelections() : (
58145                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
58146             };
58147         }
58148         return false;
58149     },
58150     
58151     
58152     onInitDrag : function(e){
58153         var data = this.dragData;
58154         this.ddel.innerHTML = this.grid.getDragDropText();
58155         this.proxy.update(this.ddel);
58156         // fire start drag?
58157     },
58158
58159     afterRepair : function(){
58160         this.dragging = false;
58161     },
58162
58163     getRepairXY : function(e, data){
58164         return false;
58165     },
58166
58167     onEndDrag : function(data, e){
58168         // fire end drag?
58169     },
58170
58171     onValidDrop : function(dd, e, id){
58172         // fire drag drop?
58173         this.hideProxy();
58174     },
58175
58176     beforeInvalidDrop : function(e, id){
58177
58178     }
58179 });/*
58180  * Based on:
58181  * Ext JS Library 1.1.1
58182  * Copyright(c) 2006-2007, Ext JS, LLC.
58183  *
58184  * Originally Released Under LGPL - original licence link has changed is not relivant.
58185  *
58186  * Fork - LGPL
58187  * <script type="text/javascript">
58188  */
58189  
58190
58191 /**
58192  * @class Roo.grid.ColumnModel
58193  * @extends Roo.util.Observable
58194  * This is the default implementation of a ColumnModel used by the Grid. It defines
58195  * the columns in the grid.
58196  * <br>Usage:<br>
58197  <pre><code>
58198  var colModel = new Roo.grid.ColumnModel([
58199         {header: "Ticker", width: 60, sortable: true, locked: true},
58200         {header: "Company Name", width: 150, sortable: true},
58201         {header: "Market Cap.", width: 100, sortable: true},
58202         {header: "$ Sales", width: 100, sortable: true, renderer: money},
58203         {header: "Employees", width: 100, sortable: true, resizable: false}
58204  ]);
58205  </code></pre>
58206  * <p>
58207  
58208  * The config options listed for this class are options which may appear in each
58209  * individual column definition.
58210  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
58211  * @constructor
58212  * @param {Object} config An Array of column config objects. See this class's
58213  * config objects for details.
58214 */
58215 Roo.grid.ColumnModel = function(config){
58216         /**
58217      * The config passed into the constructor
58218      */
58219     this.config = config;
58220     this.lookup = {};
58221
58222     // if no id, create one
58223     // if the column does not have a dataIndex mapping,
58224     // map it to the order it is in the config
58225     for(var i = 0, len = config.length; i < len; i++){
58226         var c = config[i];
58227         if(typeof c.dataIndex == "undefined"){
58228             c.dataIndex = i;
58229         }
58230         if(typeof c.renderer == "string"){
58231             c.renderer = Roo.util.Format[c.renderer];
58232         }
58233         if(typeof c.id == "undefined"){
58234             c.id = Roo.id();
58235         }
58236         if(c.editor && c.editor.xtype){
58237             c.editor  = Roo.factory(c.editor, Roo.grid);
58238         }
58239         if(c.editor && c.editor.isFormField){
58240             c.editor = new Roo.grid.GridEditor(c.editor);
58241         }
58242         this.lookup[c.id] = c;
58243     }
58244
58245     /**
58246      * The width of columns which have no width specified (defaults to 100)
58247      * @type Number
58248      */
58249     this.defaultWidth = 100;
58250
58251     /**
58252      * Default sortable of columns which have no sortable specified (defaults to false)
58253      * @type Boolean
58254      */
58255     this.defaultSortable = false;
58256
58257     this.addEvents({
58258         /**
58259              * @event widthchange
58260              * Fires when the width of a column changes.
58261              * @param {ColumnModel} this
58262              * @param {Number} columnIndex The column index
58263              * @param {Number} newWidth The new width
58264              */
58265             "widthchange": true,
58266         /**
58267              * @event headerchange
58268              * Fires when the text of a header changes.
58269              * @param {ColumnModel} this
58270              * @param {Number} columnIndex The column index
58271              * @param {Number} newText The new header text
58272              */
58273             "headerchange": true,
58274         /**
58275              * @event hiddenchange
58276              * Fires when a column is hidden or "unhidden".
58277              * @param {ColumnModel} this
58278              * @param {Number} columnIndex The column index
58279              * @param {Boolean} hidden true if hidden, false otherwise
58280              */
58281             "hiddenchange": true,
58282             /**
58283          * @event columnmoved
58284          * Fires when a column is moved.
58285          * @param {ColumnModel} this
58286          * @param {Number} oldIndex
58287          * @param {Number} newIndex
58288          */
58289         "columnmoved" : true,
58290         /**
58291          * @event columlockchange
58292          * Fires when a column's locked state is changed
58293          * @param {ColumnModel} this
58294          * @param {Number} colIndex
58295          * @param {Boolean} locked true if locked
58296          */
58297         "columnlockchange" : true
58298     });
58299     Roo.grid.ColumnModel.superclass.constructor.call(this);
58300 };
58301 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
58302     /**
58303      * @cfg {String} header The header text to display in the Grid view.
58304      */
58305     /**
58306      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
58307      * {@link Roo.data.Record} definition from which to draw the column's value. If not
58308      * specified, the column's index is used as an index into the Record's data Array.
58309      */
58310     /**
58311      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
58312      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
58313      */
58314     /**
58315      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
58316      * Defaults to the value of the {@link #defaultSortable} property.
58317      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
58318      */
58319     /**
58320      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
58321      */
58322     /**
58323      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
58324      */
58325     /**
58326      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
58327      */
58328     /**
58329      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
58330      */
58331     /**
58332      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
58333      * given the cell's data value. See {@link #setRenderer}. If not specified, the
58334      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
58335      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
58336      */
58337        /**
58338      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
58339      */
58340     /**
58341      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
58342      */
58343     /**
58344      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
58345      */
58346     /**
58347      * @cfg {String} cursor (Optional)
58348      */
58349     /**
58350      * @cfg {String} tooltip (Optional)
58351      */
58352     /**
58353      * @cfg {Number} xs (Optional)
58354      */
58355     /**
58356      * @cfg {Number} sm (Optional)
58357      */
58358     /**
58359      * @cfg {Number} md (Optional)
58360      */
58361     /**
58362      * @cfg {Number} lg (Optional)
58363      */
58364     /**
58365      * Returns the id of the column at the specified index.
58366      * @param {Number} index The column index
58367      * @return {String} the id
58368      */
58369     getColumnId : function(index){
58370         return this.config[index].id;
58371     },
58372
58373     /**
58374      * Returns the column for a specified id.
58375      * @param {String} id The column id
58376      * @return {Object} the column
58377      */
58378     getColumnById : function(id){
58379         return this.lookup[id];
58380     },
58381
58382     
58383     /**
58384      * Returns the column for a specified dataIndex.
58385      * @param {String} dataIndex The column dataIndex
58386      * @return {Object|Boolean} the column or false if not found
58387      */
58388     getColumnByDataIndex: function(dataIndex){
58389         var index = this.findColumnIndex(dataIndex);
58390         return index > -1 ? this.config[index] : false;
58391     },
58392     
58393     /**
58394      * Returns the index for a specified column id.
58395      * @param {String} id The column id
58396      * @return {Number} the index, or -1 if not found
58397      */
58398     getIndexById : function(id){
58399         for(var i = 0, len = this.config.length; i < len; i++){
58400             if(this.config[i].id == id){
58401                 return i;
58402             }
58403         }
58404         return -1;
58405     },
58406     
58407     /**
58408      * Returns the index for a specified column dataIndex.
58409      * @param {String} dataIndex The column dataIndex
58410      * @return {Number} the index, or -1 if not found
58411      */
58412     
58413     findColumnIndex : function(dataIndex){
58414         for(var i = 0, len = this.config.length; i < len; i++){
58415             if(this.config[i].dataIndex == dataIndex){
58416                 return i;
58417             }
58418         }
58419         return -1;
58420     },
58421     
58422     
58423     moveColumn : function(oldIndex, newIndex){
58424         var c = this.config[oldIndex];
58425         this.config.splice(oldIndex, 1);
58426         this.config.splice(newIndex, 0, c);
58427         this.dataMap = null;
58428         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58429     },
58430
58431     isLocked : function(colIndex){
58432         return this.config[colIndex].locked === true;
58433     },
58434
58435     setLocked : function(colIndex, value, suppressEvent){
58436         if(this.isLocked(colIndex) == value){
58437             return;
58438         }
58439         this.config[colIndex].locked = value;
58440         if(!suppressEvent){
58441             this.fireEvent("columnlockchange", this, colIndex, value);
58442         }
58443     },
58444
58445     getTotalLockedWidth : function(){
58446         var totalWidth = 0;
58447         for(var i = 0; i < this.config.length; i++){
58448             if(this.isLocked(i) && !this.isHidden(i)){
58449                 this.totalWidth += this.getColumnWidth(i);
58450             }
58451         }
58452         return totalWidth;
58453     },
58454
58455     getLockedCount : function(){
58456         for(var i = 0, len = this.config.length; i < len; i++){
58457             if(!this.isLocked(i)){
58458                 return i;
58459             }
58460         }
58461         
58462         return this.config.length;
58463     },
58464
58465     /**
58466      * Returns the number of columns.
58467      * @return {Number}
58468      */
58469     getColumnCount : function(visibleOnly){
58470         if(visibleOnly === true){
58471             var c = 0;
58472             for(var i = 0, len = this.config.length; i < len; i++){
58473                 if(!this.isHidden(i)){
58474                     c++;
58475                 }
58476             }
58477             return c;
58478         }
58479         return this.config.length;
58480     },
58481
58482     /**
58483      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58484      * @param {Function} fn
58485      * @param {Object} scope (optional)
58486      * @return {Array} result
58487      */
58488     getColumnsBy : function(fn, scope){
58489         var r = [];
58490         for(var i = 0, len = this.config.length; i < len; i++){
58491             var c = this.config[i];
58492             if(fn.call(scope||this, c, i) === true){
58493                 r[r.length] = c;
58494             }
58495         }
58496         return r;
58497     },
58498
58499     /**
58500      * Returns true if the specified column is sortable.
58501      * @param {Number} col The column index
58502      * @return {Boolean}
58503      */
58504     isSortable : function(col){
58505         if(typeof this.config[col].sortable == "undefined"){
58506             return this.defaultSortable;
58507         }
58508         return this.config[col].sortable;
58509     },
58510
58511     /**
58512      * Returns the rendering (formatting) function defined for the column.
58513      * @param {Number} col The column index.
58514      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58515      */
58516     getRenderer : function(col){
58517         if(!this.config[col].renderer){
58518             return Roo.grid.ColumnModel.defaultRenderer;
58519         }
58520         return this.config[col].renderer;
58521     },
58522
58523     /**
58524      * Sets the rendering (formatting) function for a column.
58525      * @param {Number} col The column index
58526      * @param {Function} fn The function to use to process the cell's raw data
58527      * to return HTML markup for the grid view. The render function is called with
58528      * the following parameters:<ul>
58529      * <li>Data value.</li>
58530      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58531      * <li>css A CSS style string to apply to the table cell.</li>
58532      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58533      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58534      * <li>Row index</li>
58535      * <li>Column index</li>
58536      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58537      */
58538     setRenderer : function(col, fn){
58539         this.config[col].renderer = fn;
58540     },
58541
58542     /**
58543      * Returns the width for the specified column.
58544      * @param {Number} col The column index
58545      * @return {Number}
58546      */
58547     getColumnWidth : function(col){
58548         return this.config[col].width * 1 || this.defaultWidth;
58549     },
58550
58551     /**
58552      * Sets the width for a column.
58553      * @param {Number} col The column index
58554      * @param {Number} width The new width
58555      */
58556     setColumnWidth : function(col, width, suppressEvent){
58557         this.config[col].width = width;
58558         this.totalWidth = null;
58559         if(!suppressEvent){
58560              this.fireEvent("widthchange", this, col, width);
58561         }
58562     },
58563
58564     /**
58565      * Returns the total width of all columns.
58566      * @param {Boolean} includeHidden True to include hidden column widths
58567      * @return {Number}
58568      */
58569     getTotalWidth : function(includeHidden){
58570         if(!this.totalWidth){
58571             this.totalWidth = 0;
58572             for(var i = 0, len = this.config.length; i < len; i++){
58573                 if(includeHidden || !this.isHidden(i)){
58574                     this.totalWidth += this.getColumnWidth(i);
58575                 }
58576             }
58577         }
58578         return this.totalWidth;
58579     },
58580
58581     /**
58582      * Returns the header for the specified column.
58583      * @param {Number} col The column index
58584      * @return {String}
58585      */
58586     getColumnHeader : function(col){
58587         return this.config[col].header;
58588     },
58589
58590     /**
58591      * Sets the header for a column.
58592      * @param {Number} col The column index
58593      * @param {String} header The new header
58594      */
58595     setColumnHeader : function(col, header){
58596         this.config[col].header = header;
58597         this.fireEvent("headerchange", this, col, header);
58598     },
58599
58600     /**
58601      * Returns the tooltip for the specified column.
58602      * @param {Number} col The column index
58603      * @return {String}
58604      */
58605     getColumnTooltip : function(col){
58606             return this.config[col].tooltip;
58607     },
58608     /**
58609      * Sets the tooltip for a column.
58610      * @param {Number} col The column index
58611      * @param {String} tooltip The new tooltip
58612      */
58613     setColumnTooltip : function(col, tooltip){
58614             this.config[col].tooltip = tooltip;
58615     },
58616
58617     /**
58618      * Returns the dataIndex for the specified column.
58619      * @param {Number} col The column index
58620      * @return {Number}
58621      */
58622     getDataIndex : function(col){
58623         return this.config[col].dataIndex;
58624     },
58625
58626     /**
58627      * Sets the dataIndex for a column.
58628      * @param {Number} col The column index
58629      * @param {Number} dataIndex The new dataIndex
58630      */
58631     setDataIndex : function(col, dataIndex){
58632         this.config[col].dataIndex = dataIndex;
58633     },
58634
58635     
58636     
58637     /**
58638      * Returns true if the cell is editable.
58639      * @param {Number} colIndex The column index
58640      * @param {Number} rowIndex The row index - this is nto actually used..?
58641      * @return {Boolean}
58642      */
58643     isCellEditable : function(colIndex, rowIndex){
58644         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58645     },
58646
58647     /**
58648      * Returns the editor defined for the cell/column.
58649      * return false or null to disable editing.
58650      * @param {Number} colIndex The column index
58651      * @param {Number} rowIndex The row index
58652      * @return {Object}
58653      */
58654     getCellEditor : function(colIndex, rowIndex){
58655         return this.config[colIndex].editor;
58656     },
58657
58658     /**
58659      * Sets if a column is editable.
58660      * @param {Number} col The column index
58661      * @param {Boolean} editable True if the column is editable
58662      */
58663     setEditable : function(col, editable){
58664         this.config[col].editable = editable;
58665     },
58666
58667
58668     /**
58669      * Returns true if the column is hidden.
58670      * @param {Number} colIndex The column index
58671      * @return {Boolean}
58672      */
58673     isHidden : function(colIndex){
58674         return this.config[colIndex].hidden;
58675     },
58676
58677
58678     /**
58679      * Returns true if the column width cannot be changed
58680      */
58681     isFixed : function(colIndex){
58682         return this.config[colIndex].fixed;
58683     },
58684
58685     /**
58686      * Returns true if the column can be resized
58687      * @return {Boolean}
58688      */
58689     isResizable : function(colIndex){
58690         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58691     },
58692     /**
58693      * Sets if a column is hidden.
58694      * @param {Number} colIndex The column index
58695      * @param {Boolean} hidden True if the column is hidden
58696      */
58697     setHidden : function(colIndex, hidden){
58698         this.config[colIndex].hidden = hidden;
58699         this.totalWidth = null;
58700         this.fireEvent("hiddenchange", this, colIndex, hidden);
58701     },
58702
58703     /**
58704      * Sets the editor for a column.
58705      * @param {Number} col The column index
58706      * @param {Object} editor The editor object
58707      */
58708     setEditor : function(col, editor){
58709         this.config[col].editor = editor;
58710     }
58711 });
58712
58713 Roo.grid.ColumnModel.defaultRenderer = function(value)
58714 {
58715     if(typeof value == "object") {
58716         return value;
58717     }
58718         if(typeof value == "string" && value.length < 1){
58719             return "&#160;";
58720         }
58721     
58722         return String.format("{0}", value);
58723 };
58724
58725 // Alias for backwards compatibility
58726 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58727 /*
58728  * Based on:
58729  * Ext JS Library 1.1.1
58730  * Copyright(c) 2006-2007, Ext JS, LLC.
58731  *
58732  * Originally Released Under LGPL - original licence link has changed is not relivant.
58733  *
58734  * Fork - LGPL
58735  * <script type="text/javascript">
58736  */
58737
58738 /**
58739  * @class Roo.grid.AbstractSelectionModel
58740  * @extends Roo.util.Observable
58741  * Abstract base class for grid SelectionModels.  It provides the interface that should be
58742  * implemented by descendant classes.  This class should not be directly instantiated.
58743  * @constructor
58744  */
58745 Roo.grid.AbstractSelectionModel = function(){
58746     this.locked = false;
58747     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58748 };
58749
58750 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58751     /** @ignore Called by the grid automatically. Do not call directly. */
58752     init : function(grid){
58753         this.grid = grid;
58754         this.initEvents();
58755     },
58756
58757     /**
58758      * Locks the selections.
58759      */
58760     lock : function(){
58761         this.locked = true;
58762     },
58763
58764     /**
58765      * Unlocks the selections.
58766      */
58767     unlock : function(){
58768         this.locked = false;
58769     },
58770
58771     /**
58772      * Returns true if the selections are locked.
58773      * @return {Boolean}
58774      */
58775     isLocked : function(){
58776         return this.locked;
58777     }
58778 });/*
58779  * Based on:
58780  * Ext JS Library 1.1.1
58781  * Copyright(c) 2006-2007, Ext JS, LLC.
58782  *
58783  * Originally Released Under LGPL - original licence link has changed is not relivant.
58784  *
58785  * Fork - LGPL
58786  * <script type="text/javascript">
58787  */
58788 /**
58789  * @extends Roo.grid.AbstractSelectionModel
58790  * @class Roo.grid.RowSelectionModel
58791  * The default SelectionModel used by {@link Roo.grid.Grid}.
58792  * It supports multiple selections and keyboard selection/navigation. 
58793  * @constructor
58794  * @param {Object} config
58795  */
58796 Roo.grid.RowSelectionModel = function(config){
58797     Roo.apply(this, config);
58798     this.selections = new Roo.util.MixedCollection(false, function(o){
58799         return o.id;
58800     });
58801
58802     this.last = false;
58803     this.lastActive = false;
58804
58805     this.addEvents({
58806         /**
58807              * @event selectionchange
58808              * Fires when the selection changes
58809              * @param {SelectionModel} this
58810              */
58811             "selectionchange" : true,
58812         /**
58813              * @event afterselectionchange
58814              * Fires after the selection changes (eg. by key press or clicking)
58815              * @param {SelectionModel} this
58816              */
58817             "afterselectionchange" : true,
58818         /**
58819              * @event beforerowselect
58820              * Fires when a row is selected being selected, return false to cancel.
58821              * @param {SelectionModel} this
58822              * @param {Number} rowIndex The selected index
58823              * @param {Boolean} keepExisting False if other selections will be cleared
58824              */
58825             "beforerowselect" : true,
58826         /**
58827              * @event rowselect
58828              * Fires when a row is selected.
58829              * @param {SelectionModel} this
58830              * @param {Number} rowIndex The selected index
58831              * @param {Roo.data.Record} r The record
58832              */
58833             "rowselect" : true,
58834         /**
58835              * @event rowdeselect
58836              * Fires when a row is deselected.
58837              * @param {SelectionModel} this
58838              * @param {Number} rowIndex The selected index
58839              */
58840         "rowdeselect" : true
58841     });
58842     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
58843     this.locked = false;
58844 };
58845
58846 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
58847     /**
58848      * @cfg {Boolean} singleSelect
58849      * True to allow selection of only one row at a time (defaults to false)
58850      */
58851     singleSelect : false,
58852
58853     // private
58854     initEvents : function(){
58855
58856         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
58857             this.grid.on("mousedown", this.handleMouseDown, this);
58858         }else{ // allow click to work like normal
58859             this.grid.on("rowclick", this.handleDragableRowClick, this);
58860         }
58861
58862         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
58863             "up" : function(e){
58864                 if(!e.shiftKey){
58865                     this.selectPrevious(e.shiftKey);
58866                 }else if(this.last !== false && this.lastActive !== false){
58867                     var last = this.last;
58868                     this.selectRange(this.last,  this.lastActive-1);
58869                     this.grid.getView().focusRow(this.lastActive);
58870                     if(last !== false){
58871                         this.last = last;
58872                     }
58873                 }else{
58874                     this.selectFirstRow();
58875                 }
58876                 this.fireEvent("afterselectionchange", this);
58877             },
58878             "down" : function(e){
58879                 if(!e.shiftKey){
58880                     this.selectNext(e.shiftKey);
58881                 }else if(this.last !== false && this.lastActive !== false){
58882                     var last = this.last;
58883                     this.selectRange(this.last,  this.lastActive+1);
58884                     this.grid.getView().focusRow(this.lastActive);
58885                     if(last !== false){
58886                         this.last = last;
58887                     }
58888                 }else{
58889                     this.selectFirstRow();
58890                 }
58891                 this.fireEvent("afterselectionchange", this);
58892             },
58893             scope: this
58894         });
58895
58896         var view = this.grid.view;
58897         view.on("refresh", this.onRefresh, this);
58898         view.on("rowupdated", this.onRowUpdated, this);
58899         view.on("rowremoved", this.onRemove, this);
58900     },
58901
58902     // private
58903     onRefresh : function(){
58904         var ds = this.grid.dataSource, i, v = this.grid.view;
58905         var s = this.selections;
58906         s.each(function(r){
58907             if((i = ds.indexOfId(r.id)) != -1){
58908                 v.onRowSelect(i);
58909                 s.add(ds.getAt(i)); // updating the selection relate data
58910             }else{
58911                 s.remove(r);
58912             }
58913         });
58914     },
58915
58916     // private
58917     onRemove : function(v, index, r){
58918         this.selections.remove(r);
58919     },
58920
58921     // private
58922     onRowUpdated : function(v, index, r){
58923         if(this.isSelected(r)){
58924             v.onRowSelect(index);
58925         }
58926     },
58927
58928     /**
58929      * Select records.
58930      * @param {Array} records The records to select
58931      * @param {Boolean} keepExisting (optional) True to keep existing selections
58932      */
58933     selectRecords : function(records, keepExisting){
58934         if(!keepExisting){
58935             this.clearSelections();
58936         }
58937         var ds = this.grid.dataSource;
58938         for(var i = 0, len = records.length; i < len; i++){
58939             this.selectRow(ds.indexOf(records[i]), true);
58940         }
58941     },
58942
58943     /**
58944      * Gets the number of selected rows.
58945      * @return {Number}
58946      */
58947     getCount : function(){
58948         return this.selections.length;
58949     },
58950
58951     /**
58952      * Selects the first row in the grid.
58953      */
58954     selectFirstRow : function(){
58955         this.selectRow(0);
58956     },
58957
58958     /**
58959      * Select the last row.
58960      * @param {Boolean} keepExisting (optional) True to keep existing selections
58961      */
58962     selectLastRow : function(keepExisting){
58963         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
58964     },
58965
58966     /**
58967      * Selects the row immediately following the last selected row.
58968      * @param {Boolean} keepExisting (optional) True to keep existing selections
58969      */
58970     selectNext : function(keepExisting){
58971         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
58972             this.selectRow(this.last+1, keepExisting);
58973             this.grid.getView().focusRow(this.last);
58974         }
58975     },
58976
58977     /**
58978      * Selects the row that precedes the last selected row.
58979      * @param {Boolean} keepExisting (optional) True to keep existing selections
58980      */
58981     selectPrevious : function(keepExisting){
58982         if(this.last){
58983             this.selectRow(this.last-1, keepExisting);
58984             this.grid.getView().focusRow(this.last);
58985         }
58986     },
58987
58988     /**
58989      * Returns the selected records
58990      * @return {Array} Array of selected records
58991      */
58992     getSelections : function(){
58993         return [].concat(this.selections.items);
58994     },
58995
58996     /**
58997      * Returns the first selected record.
58998      * @return {Record}
58999      */
59000     getSelected : function(){
59001         return this.selections.itemAt(0);
59002     },
59003
59004
59005     /**
59006      * Clears all selections.
59007      */
59008     clearSelections : function(fast){
59009         if(this.locked) {
59010             return;
59011         }
59012         if(fast !== true){
59013             var ds = this.grid.dataSource;
59014             var s = this.selections;
59015             s.each(function(r){
59016                 this.deselectRow(ds.indexOfId(r.id));
59017             }, this);
59018             s.clear();
59019         }else{
59020             this.selections.clear();
59021         }
59022         this.last = false;
59023     },
59024
59025
59026     /**
59027      * Selects all rows.
59028      */
59029     selectAll : function(){
59030         if(this.locked) {
59031             return;
59032         }
59033         this.selections.clear();
59034         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
59035             this.selectRow(i, true);
59036         }
59037     },
59038
59039     /**
59040      * Returns True if there is a selection.
59041      * @return {Boolean}
59042      */
59043     hasSelection : function(){
59044         return this.selections.length > 0;
59045     },
59046
59047     /**
59048      * Returns True if the specified row is selected.
59049      * @param {Number/Record} record The record or index of the record to check
59050      * @return {Boolean}
59051      */
59052     isSelected : function(index){
59053         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
59054         return (r && this.selections.key(r.id) ? true : false);
59055     },
59056
59057     /**
59058      * Returns True if the specified record id is selected.
59059      * @param {String} id The id of record to check
59060      * @return {Boolean}
59061      */
59062     isIdSelected : function(id){
59063         return (this.selections.key(id) ? true : false);
59064     },
59065
59066     // private
59067     handleMouseDown : function(e, t){
59068         var view = this.grid.getView(), rowIndex;
59069         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
59070             return;
59071         };
59072         if(e.shiftKey && this.last !== false){
59073             var last = this.last;
59074             this.selectRange(last, rowIndex, e.ctrlKey);
59075             this.last = last; // reset the last
59076             view.focusRow(rowIndex);
59077         }else{
59078             var isSelected = this.isSelected(rowIndex);
59079             if(e.button !== 0 && isSelected){
59080                 view.focusRow(rowIndex);
59081             }else if(e.ctrlKey && isSelected){
59082                 this.deselectRow(rowIndex);
59083             }else if(!isSelected){
59084                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
59085                 view.focusRow(rowIndex);
59086             }
59087         }
59088         this.fireEvent("afterselectionchange", this);
59089     },
59090     // private
59091     handleDragableRowClick :  function(grid, rowIndex, e) 
59092     {
59093         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
59094             this.selectRow(rowIndex, false);
59095             grid.view.focusRow(rowIndex);
59096              this.fireEvent("afterselectionchange", this);
59097         }
59098     },
59099     
59100     /**
59101      * Selects multiple rows.
59102      * @param {Array} rows Array of the indexes of the row to select
59103      * @param {Boolean} keepExisting (optional) True to keep existing selections
59104      */
59105     selectRows : function(rows, keepExisting){
59106         if(!keepExisting){
59107             this.clearSelections();
59108         }
59109         for(var i = 0, len = rows.length; i < len; i++){
59110             this.selectRow(rows[i], true);
59111         }
59112     },
59113
59114     /**
59115      * Selects a range of rows. All rows in between startRow and endRow are also selected.
59116      * @param {Number} startRow The index of the first row in the range
59117      * @param {Number} endRow The index of the last row in the range
59118      * @param {Boolean} keepExisting (optional) True to retain existing selections
59119      */
59120     selectRange : function(startRow, endRow, keepExisting){
59121         if(this.locked) {
59122             return;
59123         }
59124         if(!keepExisting){
59125             this.clearSelections();
59126         }
59127         if(startRow <= endRow){
59128             for(var i = startRow; i <= endRow; i++){
59129                 this.selectRow(i, true);
59130             }
59131         }else{
59132             for(var i = startRow; i >= endRow; i--){
59133                 this.selectRow(i, true);
59134             }
59135         }
59136     },
59137
59138     /**
59139      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
59140      * @param {Number} startRow The index of the first row in the range
59141      * @param {Number} endRow The index of the last row in the range
59142      */
59143     deselectRange : function(startRow, endRow, preventViewNotify){
59144         if(this.locked) {
59145             return;
59146         }
59147         for(var i = startRow; i <= endRow; i++){
59148             this.deselectRow(i, preventViewNotify);
59149         }
59150     },
59151
59152     /**
59153      * Selects a row.
59154      * @param {Number} row The index of the row to select
59155      * @param {Boolean} keepExisting (optional) True to keep existing selections
59156      */
59157     selectRow : function(index, keepExisting, preventViewNotify){
59158         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
59159             return;
59160         }
59161         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
59162             if(!keepExisting || this.singleSelect){
59163                 this.clearSelections();
59164             }
59165             var r = this.grid.dataSource.getAt(index);
59166             this.selections.add(r);
59167             this.last = this.lastActive = index;
59168             if(!preventViewNotify){
59169                 this.grid.getView().onRowSelect(index);
59170             }
59171             this.fireEvent("rowselect", this, index, r);
59172             this.fireEvent("selectionchange", this);
59173         }
59174     },
59175
59176     /**
59177      * Deselects a row.
59178      * @param {Number} row The index of the row to deselect
59179      */
59180     deselectRow : function(index, preventViewNotify){
59181         if(this.locked) {
59182             return;
59183         }
59184         if(this.last == index){
59185             this.last = false;
59186         }
59187         if(this.lastActive == index){
59188             this.lastActive = false;
59189         }
59190         var r = this.grid.dataSource.getAt(index);
59191         this.selections.remove(r);
59192         if(!preventViewNotify){
59193             this.grid.getView().onRowDeselect(index);
59194         }
59195         this.fireEvent("rowdeselect", this, index);
59196         this.fireEvent("selectionchange", this);
59197     },
59198
59199     // private
59200     restoreLast : function(){
59201         if(this._last){
59202             this.last = this._last;
59203         }
59204     },
59205
59206     // private
59207     acceptsNav : function(row, col, cm){
59208         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59209     },
59210
59211     // private
59212     onEditorKey : function(field, e){
59213         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
59214         if(k == e.TAB){
59215             e.stopEvent();
59216             ed.completeEdit();
59217             if(e.shiftKey){
59218                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59219             }else{
59220                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59221             }
59222         }else if(k == e.ENTER && !e.ctrlKey){
59223             e.stopEvent();
59224             ed.completeEdit();
59225             if(e.shiftKey){
59226                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
59227             }else{
59228                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
59229             }
59230         }else if(k == e.ESC){
59231             ed.cancelEdit();
59232         }
59233         if(newCell){
59234             g.startEditing(newCell[0], newCell[1]);
59235         }
59236     }
59237 });/*
59238  * Based on:
59239  * Ext JS Library 1.1.1
59240  * Copyright(c) 2006-2007, Ext JS, LLC.
59241  *
59242  * Originally Released Under LGPL - original licence link has changed is not relivant.
59243  *
59244  * Fork - LGPL
59245  * <script type="text/javascript">
59246  */
59247 /**
59248  * @class Roo.grid.CellSelectionModel
59249  * @extends Roo.grid.AbstractSelectionModel
59250  * This class provides the basic implementation for cell selection in a grid.
59251  * @constructor
59252  * @param {Object} config The object containing the configuration of this model.
59253  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
59254  */
59255 Roo.grid.CellSelectionModel = function(config){
59256     Roo.apply(this, config);
59257
59258     this.selection = null;
59259
59260     this.addEvents({
59261         /**
59262              * @event beforerowselect
59263              * Fires before a cell is selected.
59264              * @param {SelectionModel} this
59265              * @param {Number} rowIndex The selected row index
59266              * @param {Number} colIndex The selected cell index
59267              */
59268             "beforecellselect" : true,
59269         /**
59270              * @event cellselect
59271              * Fires when a cell is selected.
59272              * @param {SelectionModel} this
59273              * @param {Number} rowIndex The selected row index
59274              * @param {Number} colIndex The selected cell index
59275              */
59276             "cellselect" : true,
59277         /**
59278              * @event selectionchange
59279              * Fires when the active selection changes.
59280              * @param {SelectionModel} this
59281              * @param {Object} selection null for no selection or an object (o) with two properties
59282                 <ul>
59283                 <li>o.record: the record object for the row the selection is in</li>
59284                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
59285                 </ul>
59286              */
59287             "selectionchange" : true,
59288         /**
59289              * @event tabend
59290              * Fires when the tab (or enter) was pressed on the last editable cell
59291              * You can use this to trigger add new row.
59292              * @param {SelectionModel} this
59293              */
59294             "tabend" : true,
59295          /**
59296              * @event beforeeditnext
59297              * Fires before the next editable sell is made active
59298              * You can use this to skip to another cell or fire the tabend
59299              *    if you set cell to false
59300              * @param {Object} eventdata object : { cell : [ row, col ] } 
59301              */
59302             "beforeeditnext" : true
59303     });
59304     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
59305 };
59306
59307 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
59308     
59309     enter_is_tab: false,
59310
59311     /** @ignore */
59312     initEvents : function(){
59313         this.grid.on("mousedown", this.handleMouseDown, this);
59314         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
59315         var view = this.grid.view;
59316         view.on("refresh", this.onViewChange, this);
59317         view.on("rowupdated", this.onRowUpdated, this);
59318         view.on("beforerowremoved", this.clearSelections, this);
59319         view.on("beforerowsinserted", this.clearSelections, this);
59320         if(this.grid.isEditor){
59321             this.grid.on("beforeedit", this.beforeEdit,  this);
59322         }
59323     },
59324
59325         //private
59326     beforeEdit : function(e){
59327         this.select(e.row, e.column, false, true, e.record);
59328     },
59329
59330         //private
59331     onRowUpdated : function(v, index, r){
59332         if(this.selection && this.selection.record == r){
59333             v.onCellSelect(index, this.selection.cell[1]);
59334         }
59335     },
59336
59337         //private
59338     onViewChange : function(){
59339         this.clearSelections(true);
59340     },
59341
59342         /**
59343          * Returns the currently selected cell,.
59344          * @return {Array} The selected cell (row, column) or null if none selected.
59345          */
59346     getSelectedCell : function(){
59347         return this.selection ? this.selection.cell : null;
59348     },
59349
59350     /**
59351      * Clears all selections.
59352      * @param {Boolean} true to prevent the gridview from being notified about the change.
59353      */
59354     clearSelections : function(preventNotify){
59355         var s = this.selection;
59356         if(s){
59357             if(preventNotify !== true){
59358                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
59359             }
59360             this.selection = null;
59361             this.fireEvent("selectionchange", this, null);
59362         }
59363     },
59364
59365     /**
59366      * Returns true if there is a selection.
59367      * @return {Boolean}
59368      */
59369     hasSelection : function(){
59370         return this.selection ? true : false;
59371     },
59372
59373     /** @ignore */
59374     handleMouseDown : function(e, t){
59375         var v = this.grid.getView();
59376         if(this.isLocked()){
59377             return;
59378         };
59379         var row = v.findRowIndex(t);
59380         var cell = v.findCellIndex(t);
59381         if(row !== false && cell !== false){
59382             this.select(row, cell);
59383         }
59384     },
59385
59386     /**
59387      * Selects a cell.
59388      * @param {Number} rowIndex
59389      * @param {Number} collIndex
59390      */
59391     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
59392         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
59393             this.clearSelections();
59394             r = r || this.grid.dataSource.getAt(rowIndex);
59395             this.selection = {
59396                 record : r,
59397                 cell : [rowIndex, colIndex]
59398             };
59399             if(!preventViewNotify){
59400                 var v = this.grid.getView();
59401                 v.onCellSelect(rowIndex, colIndex);
59402                 if(preventFocus !== true){
59403                     v.focusCell(rowIndex, colIndex);
59404                 }
59405             }
59406             this.fireEvent("cellselect", this, rowIndex, colIndex);
59407             this.fireEvent("selectionchange", this, this.selection);
59408         }
59409     },
59410
59411         //private
59412     isSelectable : function(rowIndex, colIndex, cm){
59413         return !cm.isHidden(colIndex);
59414     },
59415
59416     /** @ignore */
59417     handleKeyDown : function(e){
59418         //Roo.log('Cell Sel Model handleKeyDown');
59419         if(!e.isNavKeyPress()){
59420             return;
59421         }
59422         var g = this.grid, s = this.selection;
59423         if(!s){
59424             e.stopEvent();
59425             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59426             if(cell){
59427                 this.select(cell[0], cell[1]);
59428             }
59429             return;
59430         }
59431         var sm = this;
59432         var walk = function(row, col, step){
59433             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59434         };
59435         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59436         var newCell;
59437
59438       
59439
59440         switch(k){
59441             case e.TAB:
59442                 // handled by onEditorKey
59443                 if (g.isEditor && g.editing) {
59444                     return;
59445                 }
59446                 if(e.shiftKey) {
59447                     newCell = walk(r, c-1, -1);
59448                 } else {
59449                     newCell = walk(r, c+1, 1);
59450                 }
59451                 break;
59452             
59453             case e.DOWN:
59454                newCell = walk(r+1, c, 1);
59455                 break;
59456             
59457             case e.UP:
59458                 newCell = walk(r-1, c, -1);
59459                 break;
59460             
59461             case e.RIGHT:
59462                 newCell = walk(r, c+1, 1);
59463                 break;
59464             
59465             case e.LEFT:
59466                 newCell = walk(r, c-1, -1);
59467                 break;
59468             
59469             case e.ENTER:
59470                 
59471                 if(g.isEditor && !g.editing){
59472                    g.startEditing(r, c);
59473                    e.stopEvent();
59474                    return;
59475                 }
59476                 
59477                 
59478              break;
59479         };
59480         if(newCell){
59481             this.select(newCell[0], newCell[1]);
59482             e.stopEvent();
59483             
59484         }
59485     },
59486
59487     acceptsNav : function(row, col, cm){
59488         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59489     },
59490     /**
59491      * Selects a cell.
59492      * @param {Number} field (not used) - as it's normally used as a listener
59493      * @param {Number} e - event - fake it by using
59494      *
59495      * var e = Roo.EventObjectImpl.prototype;
59496      * e.keyCode = e.TAB
59497      *
59498      * 
59499      */
59500     onEditorKey : function(field, e){
59501         
59502         var k = e.getKey(),
59503             newCell,
59504             g = this.grid,
59505             ed = g.activeEditor,
59506             forward = false;
59507         ///Roo.log('onEditorKey' + k);
59508         
59509         
59510         if (this.enter_is_tab && k == e.ENTER) {
59511             k = e.TAB;
59512         }
59513         
59514         if(k == e.TAB){
59515             if(e.shiftKey){
59516                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59517             }else{
59518                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59519                 forward = true;
59520             }
59521             
59522             e.stopEvent();
59523             
59524         } else if(k == e.ENTER &&  !e.ctrlKey){
59525             ed.completeEdit();
59526             e.stopEvent();
59527             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59528         
59529                 } else if(k == e.ESC){
59530             ed.cancelEdit();
59531         }
59532                 
59533         if (newCell) {
59534             var ecall = { cell : newCell, forward : forward };
59535             this.fireEvent('beforeeditnext', ecall );
59536             newCell = ecall.cell;
59537                         forward = ecall.forward;
59538         }
59539                 
59540         if(newCell){
59541             //Roo.log('next cell after edit');
59542             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59543         } else if (forward) {
59544             // tabbed past last
59545             this.fireEvent.defer(100, this, ['tabend',this]);
59546         }
59547     }
59548 });/*
59549  * Based on:
59550  * Ext JS Library 1.1.1
59551  * Copyright(c) 2006-2007, Ext JS, LLC.
59552  *
59553  * Originally Released Under LGPL - original licence link has changed is not relivant.
59554  *
59555  * Fork - LGPL
59556  * <script type="text/javascript">
59557  */
59558  
59559 /**
59560  * @class Roo.grid.EditorGrid
59561  * @extends Roo.grid.Grid
59562  * Class for creating and editable grid.
59563  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59564  * The container MUST have some type of size defined for the grid to fill. The container will be 
59565  * automatically set to position relative if it isn't already.
59566  * @param {Object} dataSource The data model to bind to
59567  * @param {Object} colModel The column model with info about this grid's columns
59568  */
59569 Roo.grid.EditorGrid = function(container, config){
59570     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59571     this.getGridEl().addClass("xedit-grid");
59572
59573     if(!this.selModel){
59574         this.selModel = new Roo.grid.CellSelectionModel();
59575     }
59576
59577     this.activeEditor = null;
59578
59579         this.addEvents({
59580             /**
59581              * @event beforeedit
59582              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59583              * <ul style="padding:5px;padding-left:16px;">
59584              * <li>grid - This grid</li>
59585              * <li>record - The record being edited</li>
59586              * <li>field - The field name being edited</li>
59587              * <li>value - The value for the field being edited.</li>
59588              * <li>row - The grid row index</li>
59589              * <li>column - The grid column index</li>
59590              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59591              * </ul>
59592              * @param {Object} e An edit event (see above for description)
59593              */
59594             "beforeedit" : true,
59595             /**
59596              * @event afteredit
59597              * Fires after a cell is edited. <br />
59598              * <ul style="padding:5px;padding-left:16px;">
59599              * <li>grid - This grid</li>
59600              * <li>record - The record being edited</li>
59601              * <li>field - The field name being edited</li>
59602              * <li>value - The value being set</li>
59603              * <li>originalValue - The original value for the field, before the edit.</li>
59604              * <li>row - The grid row index</li>
59605              * <li>column - The grid column index</li>
59606              * </ul>
59607              * @param {Object} e An edit event (see above for description)
59608              */
59609             "afteredit" : true,
59610             /**
59611              * @event validateedit
59612              * Fires after a cell is edited, but before the value is set in the record. 
59613          * You can use this to modify the value being set in the field, Return false
59614              * to cancel the change. The edit event object has the following properties <br />
59615              * <ul style="padding:5px;padding-left:16px;">
59616          * <li>editor - This editor</li>
59617              * <li>grid - This grid</li>
59618              * <li>record - The record being edited</li>
59619              * <li>field - The field name being edited</li>
59620              * <li>value - The value being set</li>
59621              * <li>originalValue - The original value for the field, before the edit.</li>
59622              * <li>row - The grid row index</li>
59623              * <li>column - The grid column index</li>
59624              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59625              * </ul>
59626              * @param {Object} e An edit event (see above for description)
59627              */
59628             "validateedit" : true
59629         });
59630     this.on("bodyscroll", this.stopEditing,  this);
59631     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59632 };
59633
59634 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59635     /**
59636      * @cfg {Number} clicksToEdit
59637      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59638      */
59639     clicksToEdit: 2,
59640
59641     // private
59642     isEditor : true,
59643     // private
59644     trackMouseOver: false, // causes very odd FF errors
59645
59646     onCellDblClick : function(g, row, col){
59647         this.startEditing(row, col);
59648     },
59649
59650     onEditComplete : function(ed, value, startValue){
59651         this.editing = false;
59652         this.activeEditor = null;
59653         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59654         var r = ed.record;
59655         var field = this.colModel.getDataIndex(ed.col);
59656         var e = {
59657             grid: this,
59658             record: r,
59659             field: field,
59660             originalValue: startValue,
59661             value: value,
59662             row: ed.row,
59663             column: ed.col,
59664             cancel:false,
59665             editor: ed
59666         };
59667         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59668         cell.show();
59669           
59670         if(String(value) !== String(startValue)){
59671             
59672             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59673                 r.set(field, e.value);
59674                 // if we are dealing with a combo box..
59675                 // then we also set the 'name' colum to be the displayField
59676                 if (ed.field.displayField && ed.field.name) {
59677                     r.set(ed.field.name, ed.field.el.dom.value);
59678                 }
59679                 
59680                 delete e.cancel; //?? why!!!
59681                 this.fireEvent("afteredit", e);
59682             }
59683         } else {
59684             this.fireEvent("afteredit", e); // always fire it!
59685         }
59686         this.view.focusCell(ed.row, ed.col);
59687     },
59688
59689     /**
59690      * Starts editing the specified for the specified row/column
59691      * @param {Number} rowIndex
59692      * @param {Number} colIndex
59693      */
59694     startEditing : function(row, col){
59695         this.stopEditing();
59696         if(this.colModel.isCellEditable(col, row)){
59697             this.view.ensureVisible(row, col, true);
59698           
59699             var r = this.dataSource.getAt(row);
59700             var field = this.colModel.getDataIndex(col);
59701             var cell = Roo.get(this.view.getCell(row,col));
59702             var e = {
59703                 grid: this,
59704                 record: r,
59705                 field: field,
59706                 value: r.data[field],
59707                 row: row,
59708                 column: col,
59709                 cancel:false 
59710             };
59711             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59712                 this.editing = true;
59713                 var ed = this.colModel.getCellEditor(col, row);
59714                 
59715                 if (!ed) {
59716                     return;
59717                 }
59718                 if(!ed.rendered){
59719                     ed.render(ed.parentEl || document.body);
59720                 }
59721                 ed.field.reset();
59722                
59723                 cell.hide();
59724                 
59725                 (function(){ // complex but required for focus issues in safari, ie and opera
59726                     ed.row = row;
59727                     ed.col = col;
59728                     ed.record = r;
59729                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59730                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
59731                     this.activeEditor = ed;
59732                     var v = r.data[field];
59733                     ed.startEdit(this.view.getCell(row, col), v);
59734                     // combo's with 'displayField and name set
59735                     if (ed.field.displayField && ed.field.name) {
59736                         ed.field.el.dom.value = r.data[ed.field.name];
59737                     }
59738                     
59739                     
59740                 }).defer(50, this);
59741             }
59742         }
59743     },
59744         
59745     /**
59746      * Stops any active editing
59747      */
59748     stopEditing : function(){
59749         if(this.activeEditor){
59750             this.activeEditor.completeEdit();
59751         }
59752         this.activeEditor = null;
59753     },
59754         
59755          /**
59756      * Called to get grid's drag proxy text, by default returns this.ddText.
59757      * @return {String}
59758      */
59759     getDragDropText : function(){
59760         var count = this.selModel.getSelectedCell() ? 1 : 0;
59761         return String.format(this.ddText, count, count == 1 ? '' : 's');
59762     }
59763         
59764 });/*
59765  * Based on:
59766  * Ext JS Library 1.1.1
59767  * Copyright(c) 2006-2007, Ext JS, LLC.
59768  *
59769  * Originally Released Under LGPL - original licence link has changed is not relivant.
59770  *
59771  * Fork - LGPL
59772  * <script type="text/javascript">
59773  */
59774
59775 // private - not really -- you end up using it !
59776 // This is a support class used internally by the Grid components
59777
59778 /**
59779  * @class Roo.grid.GridEditor
59780  * @extends Roo.Editor
59781  * Class for creating and editable grid elements.
59782  * @param {Object} config any settings (must include field)
59783  */
59784 Roo.grid.GridEditor = function(field, config){
59785     if (!config && field.field) {
59786         config = field;
59787         field = Roo.factory(config.field, Roo.form);
59788     }
59789     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59790     field.monitorTab = false;
59791 };
59792
59793 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59794     
59795     /**
59796      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59797      */
59798     
59799     alignment: "tl-tl",
59800     autoSize: "width",
59801     hideEl : false,
59802     cls: "x-small-editor x-grid-editor",
59803     shim:false,
59804     shadow:"frame"
59805 });/*
59806  * Based on:
59807  * Ext JS Library 1.1.1
59808  * Copyright(c) 2006-2007, Ext JS, LLC.
59809  *
59810  * Originally Released Under LGPL - original licence link has changed is not relivant.
59811  *
59812  * Fork - LGPL
59813  * <script type="text/javascript">
59814  */
59815   
59816
59817   
59818 Roo.grid.PropertyRecord = Roo.data.Record.create([
59819     {name:'name',type:'string'},  'value'
59820 ]);
59821
59822
59823 Roo.grid.PropertyStore = function(grid, source){
59824     this.grid = grid;
59825     this.store = new Roo.data.Store({
59826         recordType : Roo.grid.PropertyRecord
59827     });
59828     this.store.on('update', this.onUpdate,  this);
59829     if(source){
59830         this.setSource(source);
59831     }
59832     Roo.grid.PropertyStore.superclass.constructor.call(this);
59833 };
59834
59835
59836
59837 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
59838     setSource : function(o){
59839         this.source = o;
59840         this.store.removeAll();
59841         var data = [];
59842         for(var k in o){
59843             if(this.isEditableValue(o[k])){
59844                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
59845             }
59846         }
59847         this.store.loadRecords({records: data}, {}, true);
59848     },
59849
59850     onUpdate : function(ds, record, type){
59851         if(type == Roo.data.Record.EDIT){
59852             var v = record.data['value'];
59853             var oldValue = record.modified['value'];
59854             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
59855                 this.source[record.id] = v;
59856                 record.commit();
59857                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
59858             }else{
59859                 record.reject();
59860             }
59861         }
59862     },
59863
59864     getProperty : function(row){
59865        return this.store.getAt(row);
59866     },
59867
59868     isEditableValue: function(val){
59869         if(val && val instanceof Date){
59870             return true;
59871         }else if(typeof val == 'object' || typeof val == 'function'){
59872             return false;
59873         }
59874         return true;
59875     },
59876
59877     setValue : function(prop, value){
59878         this.source[prop] = value;
59879         this.store.getById(prop).set('value', value);
59880     },
59881
59882     getSource : function(){
59883         return this.source;
59884     }
59885 });
59886
59887 Roo.grid.PropertyColumnModel = function(grid, store){
59888     this.grid = grid;
59889     var g = Roo.grid;
59890     g.PropertyColumnModel.superclass.constructor.call(this, [
59891         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
59892         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
59893     ]);
59894     this.store = store;
59895     this.bselect = Roo.DomHelper.append(document.body, {
59896         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
59897             {tag: 'option', value: 'true', html: 'true'},
59898             {tag: 'option', value: 'false', html: 'false'}
59899         ]
59900     });
59901     Roo.id(this.bselect);
59902     var f = Roo.form;
59903     this.editors = {
59904         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
59905         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
59906         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
59907         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
59908         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
59909     };
59910     this.renderCellDelegate = this.renderCell.createDelegate(this);
59911     this.renderPropDelegate = this.renderProp.createDelegate(this);
59912 };
59913
59914 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
59915     
59916     
59917     nameText : 'Name',
59918     valueText : 'Value',
59919     
59920     dateFormat : 'm/j/Y',
59921     
59922     
59923     renderDate : function(dateVal){
59924         return dateVal.dateFormat(this.dateFormat);
59925     },
59926
59927     renderBool : function(bVal){
59928         return bVal ? 'true' : 'false';
59929     },
59930
59931     isCellEditable : function(colIndex, rowIndex){
59932         return colIndex == 1;
59933     },
59934
59935     getRenderer : function(col){
59936         return col == 1 ?
59937             this.renderCellDelegate : this.renderPropDelegate;
59938     },
59939
59940     renderProp : function(v){
59941         return this.getPropertyName(v);
59942     },
59943
59944     renderCell : function(val){
59945         var rv = val;
59946         if(val instanceof Date){
59947             rv = this.renderDate(val);
59948         }else if(typeof val == 'boolean'){
59949             rv = this.renderBool(val);
59950         }
59951         return Roo.util.Format.htmlEncode(rv);
59952     },
59953
59954     getPropertyName : function(name){
59955         var pn = this.grid.propertyNames;
59956         return pn && pn[name] ? pn[name] : name;
59957     },
59958
59959     getCellEditor : function(colIndex, rowIndex){
59960         var p = this.store.getProperty(rowIndex);
59961         var n = p.data['name'], val = p.data['value'];
59962         
59963         if(typeof(this.grid.customEditors[n]) == 'string'){
59964             return this.editors[this.grid.customEditors[n]];
59965         }
59966         if(typeof(this.grid.customEditors[n]) != 'undefined'){
59967             return this.grid.customEditors[n];
59968         }
59969         if(val instanceof Date){
59970             return this.editors['date'];
59971         }else if(typeof val == 'number'){
59972             return this.editors['number'];
59973         }else if(typeof val == 'boolean'){
59974             return this.editors['boolean'];
59975         }else{
59976             return this.editors['string'];
59977         }
59978     }
59979 });
59980
59981 /**
59982  * @class Roo.grid.PropertyGrid
59983  * @extends Roo.grid.EditorGrid
59984  * This class represents the  interface of a component based property grid control.
59985  * <br><br>Usage:<pre><code>
59986  var grid = new Roo.grid.PropertyGrid("my-container-id", {
59987       
59988  });
59989  // set any options
59990  grid.render();
59991  * </code></pre>
59992   
59993  * @constructor
59994  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59995  * The container MUST have some type of size defined for the grid to fill. The container will be
59996  * automatically set to position relative if it isn't already.
59997  * @param {Object} config A config object that sets properties on this grid.
59998  */
59999 Roo.grid.PropertyGrid = function(container, config){
60000     config = config || {};
60001     var store = new Roo.grid.PropertyStore(this);
60002     this.store = store;
60003     var cm = new Roo.grid.PropertyColumnModel(this, store);
60004     store.store.sort('name', 'ASC');
60005     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
60006         ds: store.store,
60007         cm: cm,
60008         enableColLock:false,
60009         enableColumnMove:false,
60010         stripeRows:false,
60011         trackMouseOver: false,
60012         clicksToEdit:1
60013     }, config));
60014     this.getGridEl().addClass('x-props-grid');
60015     this.lastEditRow = null;
60016     this.on('columnresize', this.onColumnResize, this);
60017     this.addEvents({
60018          /**
60019              * @event beforepropertychange
60020              * Fires before a property changes (return false to stop?)
60021              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60022              * @param {String} id Record Id
60023              * @param {String} newval New Value
60024          * @param {String} oldval Old Value
60025              */
60026         "beforepropertychange": true,
60027         /**
60028              * @event propertychange
60029              * Fires after a property changes
60030              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60031              * @param {String} id Record Id
60032              * @param {String} newval New Value
60033          * @param {String} oldval Old Value
60034              */
60035         "propertychange": true
60036     });
60037     this.customEditors = this.customEditors || {};
60038 };
60039 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
60040     
60041      /**
60042      * @cfg {Object} customEditors map of colnames=> custom editors.
60043      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
60044      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
60045      * false disables editing of the field.
60046          */
60047     
60048       /**
60049      * @cfg {Object} propertyNames map of property Names to their displayed value
60050          */
60051     
60052     render : function(){
60053         Roo.grid.PropertyGrid.superclass.render.call(this);
60054         this.autoSize.defer(100, this);
60055     },
60056
60057     autoSize : function(){
60058         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
60059         if(this.view){
60060             this.view.fitColumns();
60061         }
60062     },
60063
60064     onColumnResize : function(){
60065         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
60066         this.autoSize();
60067     },
60068     /**
60069      * Sets the data for the Grid
60070      * accepts a Key => Value object of all the elements avaiable.
60071      * @param {Object} data  to appear in grid.
60072      */
60073     setSource : function(source){
60074         this.store.setSource(source);
60075         //this.autoSize();
60076     },
60077     /**
60078      * Gets all the data from the grid.
60079      * @return {Object} data  data stored in grid
60080      */
60081     getSource : function(){
60082         return this.store.getSource();
60083     }
60084 });/*
60085   
60086  * Licence LGPL
60087  
60088  */
60089  
60090 /**
60091  * @class Roo.grid.Calendar
60092  * @extends Roo.util.Grid
60093  * This class extends the Grid to provide a calendar widget
60094  * <br><br>Usage:<pre><code>
60095  var grid = new Roo.grid.Calendar("my-container-id", {
60096      ds: myDataStore,
60097      cm: myColModel,
60098      selModel: mySelectionModel,
60099      autoSizeColumns: true,
60100      monitorWindowResize: false,
60101      trackMouseOver: true
60102      eventstore : real data store..
60103  });
60104  // set any options
60105  grid.render();
60106   
60107   * @constructor
60108  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60109  * The container MUST have some type of size defined for the grid to fill. The container will be
60110  * automatically set to position relative if it isn't already.
60111  * @param {Object} config A config object that sets properties on this grid.
60112  */
60113 Roo.grid.Calendar = function(container, config){
60114         // initialize the container
60115         this.container = Roo.get(container);
60116         this.container.update("");
60117         this.container.setStyle("overflow", "hidden");
60118     this.container.addClass('x-grid-container');
60119
60120     this.id = this.container.id;
60121
60122     Roo.apply(this, config);
60123     // check and correct shorthanded configs
60124     
60125     var rows = [];
60126     var d =1;
60127     for (var r = 0;r < 6;r++) {
60128         
60129         rows[r]=[];
60130         for (var c =0;c < 7;c++) {
60131             rows[r][c]= '';
60132         }
60133     }
60134     if (this.eventStore) {
60135         this.eventStore= Roo.factory(this.eventStore, Roo.data);
60136         this.eventStore.on('load',this.onLoad, this);
60137         this.eventStore.on('beforeload',this.clearEvents, this);
60138          
60139     }
60140     
60141     this.dataSource = new Roo.data.Store({
60142             proxy: new Roo.data.MemoryProxy(rows),
60143             reader: new Roo.data.ArrayReader({}, [
60144                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
60145     });
60146
60147     this.dataSource.load();
60148     this.ds = this.dataSource;
60149     this.ds.xmodule = this.xmodule || false;
60150     
60151     
60152     var cellRender = function(v,x,r)
60153     {
60154         return String.format(
60155             '<div class="fc-day  fc-widget-content"><div>' +
60156                 '<div class="fc-event-container"></div>' +
60157                 '<div class="fc-day-number">{0}</div>'+
60158                 
60159                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
60160             '</div></div>', v);
60161     
60162     }
60163     
60164     
60165     this.colModel = new Roo.grid.ColumnModel( [
60166         {
60167             xtype: 'ColumnModel',
60168             xns: Roo.grid,
60169             dataIndex : 'weekday0',
60170             header : 'Sunday',
60171             renderer : cellRender
60172         },
60173         {
60174             xtype: 'ColumnModel',
60175             xns: Roo.grid,
60176             dataIndex : 'weekday1',
60177             header : 'Monday',
60178             renderer : cellRender
60179         },
60180         {
60181             xtype: 'ColumnModel',
60182             xns: Roo.grid,
60183             dataIndex : 'weekday2',
60184             header : 'Tuesday',
60185             renderer : cellRender
60186         },
60187         {
60188             xtype: 'ColumnModel',
60189             xns: Roo.grid,
60190             dataIndex : 'weekday3',
60191             header : 'Wednesday',
60192             renderer : cellRender
60193         },
60194         {
60195             xtype: 'ColumnModel',
60196             xns: Roo.grid,
60197             dataIndex : 'weekday4',
60198             header : 'Thursday',
60199             renderer : cellRender
60200         },
60201         {
60202             xtype: 'ColumnModel',
60203             xns: Roo.grid,
60204             dataIndex : 'weekday5',
60205             header : 'Friday',
60206             renderer : cellRender
60207         },
60208         {
60209             xtype: 'ColumnModel',
60210             xns: Roo.grid,
60211             dataIndex : 'weekday6',
60212             header : 'Saturday',
60213             renderer : cellRender
60214         }
60215     ]);
60216     this.cm = this.colModel;
60217     this.cm.xmodule = this.xmodule || false;
60218  
60219         
60220           
60221     //this.selModel = new Roo.grid.CellSelectionModel();
60222     //this.sm = this.selModel;
60223     //this.selModel.init(this);
60224     
60225     
60226     if(this.width){
60227         this.container.setWidth(this.width);
60228     }
60229
60230     if(this.height){
60231         this.container.setHeight(this.height);
60232     }
60233     /** @private */
60234         this.addEvents({
60235         // raw events
60236         /**
60237          * @event click
60238          * The raw click event for the entire grid.
60239          * @param {Roo.EventObject} e
60240          */
60241         "click" : true,
60242         /**
60243          * @event dblclick
60244          * The raw dblclick event for the entire grid.
60245          * @param {Roo.EventObject} e
60246          */
60247         "dblclick" : true,
60248         /**
60249          * @event contextmenu
60250          * The raw contextmenu event for the entire grid.
60251          * @param {Roo.EventObject} e
60252          */
60253         "contextmenu" : true,
60254         /**
60255          * @event mousedown
60256          * The raw mousedown event for the entire grid.
60257          * @param {Roo.EventObject} e
60258          */
60259         "mousedown" : true,
60260         /**
60261          * @event mouseup
60262          * The raw mouseup event for the entire grid.
60263          * @param {Roo.EventObject} e
60264          */
60265         "mouseup" : true,
60266         /**
60267          * @event mouseover
60268          * The raw mouseover event for the entire grid.
60269          * @param {Roo.EventObject} e
60270          */
60271         "mouseover" : true,
60272         /**
60273          * @event mouseout
60274          * The raw mouseout event for the entire grid.
60275          * @param {Roo.EventObject} e
60276          */
60277         "mouseout" : true,
60278         /**
60279          * @event keypress
60280          * The raw keypress event for the entire grid.
60281          * @param {Roo.EventObject} e
60282          */
60283         "keypress" : true,
60284         /**
60285          * @event keydown
60286          * The raw keydown event for the entire grid.
60287          * @param {Roo.EventObject} e
60288          */
60289         "keydown" : true,
60290
60291         // custom events
60292
60293         /**
60294          * @event cellclick
60295          * Fires when a cell is clicked
60296          * @param {Grid} this
60297          * @param {Number} rowIndex
60298          * @param {Number} columnIndex
60299          * @param {Roo.EventObject} e
60300          */
60301         "cellclick" : true,
60302         /**
60303          * @event celldblclick
60304          * Fires when a cell is double clicked
60305          * @param {Grid} this
60306          * @param {Number} rowIndex
60307          * @param {Number} columnIndex
60308          * @param {Roo.EventObject} e
60309          */
60310         "celldblclick" : true,
60311         /**
60312          * @event rowclick
60313          * Fires when a row is clicked
60314          * @param {Grid} this
60315          * @param {Number} rowIndex
60316          * @param {Roo.EventObject} e
60317          */
60318         "rowclick" : true,
60319         /**
60320          * @event rowdblclick
60321          * Fires when a row is double clicked
60322          * @param {Grid} this
60323          * @param {Number} rowIndex
60324          * @param {Roo.EventObject} e
60325          */
60326         "rowdblclick" : true,
60327         /**
60328          * @event headerclick
60329          * Fires when a header is clicked
60330          * @param {Grid} this
60331          * @param {Number} columnIndex
60332          * @param {Roo.EventObject} e
60333          */
60334         "headerclick" : true,
60335         /**
60336          * @event headerdblclick
60337          * Fires when a header cell is double clicked
60338          * @param {Grid} this
60339          * @param {Number} columnIndex
60340          * @param {Roo.EventObject} e
60341          */
60342         "headerdblclick" : true,
60343         /**
60344          * @event rowcontextmenu
60345          * Fires when a row is right clicked
60346          * @param {Grid} this
60347          * @param {Number} rowIndex
60348          * @param {Roo.EventObject} e
60349          */
60350         "rowcontextmenu" : true,
60351         /**
60352          * @event cellcontextmenu
60353          * Fires when a cell is right clicked
60354          * @param {Grid} this
60355          * @param {Number} rowIndex
60356          * @param {Number} cellIndex
60357          * @param {Roo.EventObject} e
60358          */
60359          "cellcontextmenu" : true,
60360         /**
60361          * @event headercontextmenu
60362          * Fires when a header is right clicked
60363          * @param {Grid} this
60364          * @param {Number} columnIndex
60365          * @param {Roo.EventObject} e
60366          */
60367         "headercontextmenu" : true,
60368         /**
60369          * @event bodyscroll
60370          * Fires when the body element is scrolled
60371          * @param {Number} scrollLeft
60372          * @param {Number} scrollTop
60373          */
60374         "bodyscroll" : true,
60375         /**
60376          * @event columnresize
60377          * Fires when the user resizes a column
60378          * @param {Number} columnIndex
60379          * @param {Number} newSize
60380          */
60381         "columnresize" : true,
60382         /**
60383          * @event columnmove
60384          * Fires when the user moves a column
60385          * @param {Number} oldIndex
60386          * @param {Number} newIndex
60387          */
60388         "columnmove" : true,
60389         /**
60390          * @event startdrag
60391          * Fires when row(s) start being dragged
60392          * @param {Grid} this
60393          * @param {Roo.GridDD} dd The drag drop object
60394          * @param {event} e The raw browser event
60395          */
60396         "startdrag" : true,
60397         /**
60398          * @event enddrag
60399          * Fires when a drag operation is complete
60400          * @param {Grid} this
60401          * @param {Roo.GridDD} dd The drag drop object
60402          * @param {event} e The raw browser event
60403          */
60404         "enddrag" : true,
60405         /**
60406          * @event dragdrop
60407          * Fires when dragged row(s) are dropped on a valid DD target
60408          * @param {Grid} this
60409          * @param {Roo.GridDD} dd The drag drop object
60410          * @param {String} targetId The target drag drop object
60411          * @param {event} e The raw browser event
60412          */
60413         "dragdrop" : true,
60414         /**
60415          * @event dragover
60416          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60417          * @param {Grid} this
60418          * @param {Roo.GridDD} dd The drag drop object
60419          * @param {String} targetId The target drag drop object
60420          * @param {event} e The raw browser event
60421          */
60422         "dragover" : true,
60423         /**
60424          * @event dragenter
60425          *  Fires when the dragged row(s) first cross another DD target while being dragged
60426          * @param {Grid} this
60427          * @param {Roo.GridDD} dd The drag drop object
60428          * @param {String} targetId The target drag drop object
60429          * @param {event} e The raw browser event
60430          */
60431         "dragenter" : true,
60432         /**
60433          * @event dragout
60434          * Fires when the dragged row(s) leave another DD target while being dragged
60435          * @param {Grid} this
60436          * @param {Roo.GridDD} dd The drag drop object
60437          * @param {String} targetId The target drag drop object
60438          * @param {event} e The raw browser event
60439          */
60440         "dragout" : true,
60441         /**
60442          * @event rowclass
60443          * Fires when a row is rendered, so you can change add a style to it.
60444          * @param {GridView} gridview   The grid view
60445          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60446          */
60447         'rowclass' : true,
60448
60449         /**
60450          * @event render
60451          * Fires when the grid is rendered
60452          * @param {Grid} grid
60453          */
60454         'render' : true,
60455             /**
60456              * @event select
60457              * Fires when a date is selected
60458              * @param {DatePicker} this
60459              * @param {Date} date The selected date
60460              */
60461         'select': true,
60462         /**
60463              * @event monthchange
60464              * Fires when the displayed month changes 
60465              * @param {DatePicker} this
60466              * @param {Date} date The selected month
60467              */
60468         'monthchange': true,
60469         /**
60470              * @event evententer
60471              * Fires when mouse over an event
60472              * @param {Calendar} this
60473              * @param {event} Event
60474              */
60475         'evententer': true,
60476         /**
60477              * @event eventleave
60478              * Fires when the mouse leaves an
60479              * @param {Calendar} this
60480              * @param {event}
60481              */
60482         'eventleave': true,
60483         /**
60484              * @event eventclick
60485              * Fires when the mouse click an
60486              * @param {Calendar} this
60487              * @param {event}
60488              */
60489         'eventclick': true,
60490         /**
60491              * @event eventrender
60492              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60493              * @param {Calendar} this
60494              * @param {data} data to be modified
60495              */
60496         'eventrender': true
60497         
60498     });
60499
60500     Roo.grid.Grid.superclass.constructor.call(this);
60501     this.on('render', function() {
60502         this.view.el.addClass('x-grid-cal'); 
60503         
60504         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60505
60506     },this);
60507     
60508     if (!Roo.grid.Calendar.style) {
60509         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60510             
60511             
60512             '.x-grid-cal .x-grid-col' :  {
60513                 height: 'auto !important',
60514                 'vertical-align': 'top'
60515             },
60516             '.x-grid-cal  .fc-event-hori' : {
60517                 height: '14px'
60518             }
60519              
60520             
60521         }, Roo.id());
60522     }
60523
60524     
60525     
60526 };
60527 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60528     /**
60529      * @cfg {Store} eventStore The store that loads events.
60530      */
60531     eventStore : 25,
60532
60533      
60534     activeDate : false,
60535     startDay : 0,
60536     autoWidth : true,
60537     monitorWindowResize : false,
60538
60539     
60540     resizeColumns : function() {
60541         var col = (this.view.el.getWidth() / 7) - 3;
60542         // loop through cols, and setWidth
60543         for(var i =0 ; i < 7 ; i++){
60544             this.cm.setColumnWidth(i, col);
60545         }
60546     },
60547      setDate :function(date) {
60548         
60549         Roo.log('setDate?');
60550         
60551         this.resizeColumns();
60552         var vd = this.activeDate;
60553         this.activeDate = date;
60554 //        if(vd && this.el){
60555 //            var t = date.getTime();
60556 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60557 //                Roo.log('using add remove');
60558 //                
60559 //                this.fireEvent('monthchange', this, date);
60560 //                
60561 //                this.cells.removeClass("fc-state-highlight");
60562 //                this.cells.each(function(c){
60563 //                   if(c.dateValue == t){
60564 //                       c.addClass("fc-state-highlight");
60565 //                       setTimeout(function(){
60566 //                            try{c.dom.firstChild.focus();}catch(e){}
60567 //                       }, 50);
60568 //                       return false;
60569 //                   }
60570 //                   return true;
60571 //                });
60572 //                return;
60573 //            }
60574 //        }
60575         
60576         var days = date.getDaysInMonth();
60577         
60578         var firstOfMonth = date.getFirstDateOfMonth();
60579         var startingPos = firstOfMonth.getDay()-this.startDay;
60580         
60581         if(startingPos < this.startDay){
60582             startingPos += 7;
60583         }
60584         
60585         var pm = date.add(Date.MONTH, -1);
60586         var prevStart = pm.getDaysInMonth()-startingPos;
60587 //        
60588         
60589         
60590         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60591         
60592         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60593         //this.cells.addClassOnOver('fc-state-hover');
60594         
60595         var cells = this.cells.elements;
60596         var textEls = this.textNodes;
60597         
60598         //Roo.each(cells, function(cell){
60599         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60600         //});
60601         
60602         days += startingPos;
60603
60604         // convert everything to numbers so it's fast
60605         var day = 86400000;
60606         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60607         //Roo.log(d);
60608         //Roo.log(pm);
60609         //Roo.log(prevStart);
60610         
60611         var today = new Date().clearTime().getTime();
60612         var sel = date.clearTime().getTime();
60613         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60614         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60615         var ddMatch = this.disabledDatesRE;
60616         var ddText = this.disabledDatesText;
60617         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60618         var ddaysText = this.disabledDaysText;
60619         var format = this.format;
60620         
60621         var setCellClass = function(cal, cell){
60622             
60623             //Roo.log('set Cell Class');
60624             cell.title = "";
60625             var t = d.getTime();
60626             
60627             //Roo.log(d);
60628             
60629             
60630             cell.dateValue = t;
60631             if(t == today){
60632                 cell.className += " fc-today";
60633                 cell.className += " fc-state-highlight";
60634                 cell.title = cal.todayText;
60635             }
60636             if(t == sel){
60637                 // disable highlight in other month..
60638                 cell.className += " fc-state-highlight";
60639                 
60640             }
60641             // disabling
60642             if(t < min) {
60643                 //cell.className = " fc-state-disabled";
60644                 cell.title = cal.minText;
60645                 return;
60646             }
60647             if(t > max) {
60648                 //cell.className = " fc-state-disabled";
60649                 cell.title = cal.maxText;
60650                 return;
60651             }
60652             if(ddays){
60653                 if(ddays.indexOf(d.getDay()) != -1){
60654                     // cell.title = ddaysText;
60655                    // cell.className = " fc-state-disabled";
60656                 }
60657             }
60658             if(ddMatch && format){
60659                 var fvalue = d.dateFormat(format);
60660                 if(ddMatch.test(fvalue)){
60661                     cell.title = ddText.replace("%0", fvalue);
60662                    cell.className = " fc-state-disabled";
60663                 }
60664             }
60665             
60666             if (!cell.initialClassName) {
60667                 cell.initialClassName = cell.dom.className;
60668             }
60669             
60670             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60671         };
60672
60673         var i = 0;
60674         
60675         for(; i < startingPos; i++) {
60676             cells[i].dayName =  (++prevStart);
60677             Roo.log(textEls[i]);
60678             d.setDate(d.getDate()+1);
60679             
60680             //cells[i].className = "fc-past fc-other-month";
60681             setCellClass(this, cells[i]);
60682         }
60683         
60684         var intDay = 0;
60685         
60686         for(; i < days; i++){
60687             intDay = i - startingPos + 1;
60688             cells[i].dayName =  (intDay);
60689             d.setDate(d.getDate()+1);
60690             
60691             cells[i].className = ''; // "x-date-active";
60692             setCellClass(this, cells[i]);
60693         }
60694         var extraDays = 0;
60695         
60696         for(; i < 42; i++) {
60697             //textEls[i].innerHTML = (++extraDays);
60698             
60699             d.setDate(d.getDate()+1);
60700             cells[i].dayName = (++extraDays);
60701             cells[i].className = "fc-future fc-other-month";
60702             setCellClass(this, cells[i]);
60703         }
60704         
60705         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60706         
60707         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60708         
60709         // this will cause all the cells to mis
60710         var rows= [];
60711         var i =0;
60712         for (var r = 0;r < 6;r++) {
60713             for (var c =0;c < 7;c++) {
60714                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60715             }    
60716         }
60717         
60718         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60719         for(i=0;i<cells.length;i++) {
60720             
60721             this.cells.elements[i].dayName = cells[i].dayName ;
60722             this.cells.elements[i].className = cells[i].className;
60723             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60724             this.cells.elements[i].title = cells[i].title ;
60725             this.cells.elements[i].dateValue = cells[i].dateValue ;
60726         }
60727         
60728         
60729         
60730         
60731         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
60732         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
60733         
60734         ////if(totalRows != 6){
60735             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
60736            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
60737        // }
60738         
60739         this.fireEvent('monthchange', this, date);
60740         
60741         
60742     },
60743  /**
60744      * Returns the grid's SelectionModel.
60745      * @return {SelectionModel}
60746      */
60747     getSelectionModel : function(){
60748         if(!this.selModel){
60749             this.selModel = new Roo.grid.CellSelectionModel();
60750         }
60751         return this.selModel;
60752     },
60753
60754     load: function() {
60755         this.eventStore.load()
60756         
60757         
60758         
60759     },
60760     
60761     findCell : function(dt) {
60762         dt = dt.clearTime().getTime();
60763         var ret = false;
60764         this.cells.each(function(c){
60765             //Roo.log("check " +c.dateValue + '?=' + dt);
60766             if(c.dateValue == dt){
60767                 ret = c;
60768                 return false;
60769             }
60770             return true;
60771         });
60772         
60773         return ret;
60774     },
60775     
60776     findCells : function(rec) {
60777         var s = rec.data.start_dt.clone().clearTime().getTime();
60778        // Roo.log(s);
60779         var e= rec.data.end_dt.clone().clearTime().getTime();
60780        // Roo.log(e);
60781         var ret = [];
60782         this.cells.each(function(c){
60783              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60784             
60785             if(c.dateValue > e){
60786                 return ;
60787             }
60788             if(c.dateValue < s){
60789                 return ;
60790             }
60791             ret.push(c);
60792         });
60793         
60794         return ret;    
60795     },
60796     
60797     findBestRow: function(cells)
60798     {
60799         var ret = 0;
60800         
60801         for (var i =0 ; i < cells.length;i++) {
60802             ret  = Math.max(cells[i].rows || 0,ret);
60803         }
60804         return ret;
60805         
60806     },
60807     
60808     
60809     addItem : function(rec)
60810     {
60811         // look for vertical location slot in
60812         var cells = this.findCells(rec);
60813         
60814         rec.row = this.findBestRow(cells);
60815         
60816         // work out the location.
60817         
60818         var crow = false;
60819         var rows = [];
60820         for(var i =0; i < cells.length; i++) {
60821             if (!crow) {
60822                 crow = {
60823                     start : cells[i],
60824                     end :  cells[i]
60825                 };
60826                 continue;
60827             }
60828             if (crow.start.getY() == cells[i].getY()) {
60829                 // on same row.
60830                 crow.end = cells[i];
60831                 continue;
60832             }
60833             // different row.
60834             rows.push(crow);
60835             crow = {
60836                 start: cells[i],
60837                 end : cells[i]
60838             };
60839             
60840         }
60841         
60842         rows.push(crow);
60843         rec.els = [];
60844         rec.rows = rows;
60845         rec.cells = cells;
60846         for (var i = 0; i < cells.length;i++) {
60847             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
60848             
60849         }
60850         
60851         
60852     },
60853     
60854     clearEvents: function() {
60855         
60856         if (!this.eventStore.getCount()) {
60857             return;
60858         }
60859         // reset number of rows in cells.
60860         Roo.each(this.cells.elements, function(c){
60861             c.rows = 0;
60862         });
60863         
60864         this.eventStore.each(function(e) {
60865             this.clearEvent(e);
60866         },this);
60867         
60868     },
60869     
60870     clearEvent : function(ev)
60871     {
60872         if (ev.els) {
60873             Roo.each(ev.els, function(el) {
60874                 el.un('mouseenter' ,this.onEventEnter, this);
60875                 el.un('mouseleave' ,this.onEventLeave, this);
60876                 el.remove();
60877             },this);
60878             ev.els = [];
60879         }
60880     },
60881     
60882     
60883     renderEvent : function(ev,ctr) {
60884         if (!ctr) {
60885              ctr = this.view.el.select('.fc-event-container',true).first();
60886         }
60887         
60888          
60889         this.clearEvent(ev);
60890             //code
60891        
60892         
60893         
60894         ev.els = [];
60895         var cells = ev.cells;
60896         var rows = ev.rows;
60897         this.fireEvent('eventrender', this, ev);
60898         
60899         for(var i =0; i < rows.length; i++) {
60900             
60901             cls = '';
60902             if (i == 0) {
60903                 cls += ' fc-event-start';
60904             }
60905             if ((i+1) == rows.length) {
60906                 cls += ' fc-event-end';
60907             }
60908             
60909             //Roo.log(ev.data);
60910             // how many rows should it span..
60911             var cg = this.eventTmpl.append(ctr,Roo.apply({
60912                 fccls : cls
60913                 
60914             }, ev.data) , true);
60915             
60916             
60917             cg.on('mouseenter' ,this.onEventEnter, this, ev);
60918             cg.on('mouseleave' ,this.onEventLeave, this, ev);
60919             cg.on('click', this.onEventClick, this, ev);
60920             
60921             ev.els.push(cg);
60922             
60923             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
60924             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
60925             //Roo.log(cg);
60926              
60927             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
60928             cg.setWidth(ebox.right - sbox.x -2);
60929         }
60930     },
60931     
60932     renderEvents: function()
60933     {   
60934         // first make sure there is enough space..
60935         
60936         if (!this.eventTmpl) {
60937             this.eventTmpl = new Roo.Template(
60938                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
60939                     '<div class="fc-event-inner">' +
60940                         '<span class="fc-event-time">{time}</span>' +
60941                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
60942                     '</div>' +
60943                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
60944                 '</div>'
60945             );
60946                 
60947         }
60948                
60949         
60950         
60951         this.cells.each(function(c) {
60952             //Roo.log(c.select('.fc-day-content div',true).first());
60953             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
60954         });
60955         
60956         var ctr = this.view.el.select('.fc-event-container',true).first();
60957         
60958         var cls;
60959         this.eventStore.each(function(ev){
60960             
60961             this.renderEvent(ev);
60962              
60963              
60964         }, this);
60965         this.view.layout();
60966         
60967     },
60968     
60969     onEventEnter: function (e, el,event,d) {
60970         this.fireEvent('evententer', this, el, event);
60971     },
60972     
60973     onEventLeave: function (e, el,event,d) {
60974         this.fireEvent('eventleave', this, el, event);
60975     },
60976     
60977     onEventClick: function (e, el,event,d) {
60978         this.fireEvent('eventclick', this, el, event);
60979     },
60980     
60981     onMonthChange: function () {
60982         this.store.load();
60983     },
60984     
60985     onLoad: function () {
60986         
60987         //Roo.log('calendar onload');
60988 //         
60989         if(this.eventStore.getCount() > 0){
60990             
60991            
60992             
60993             this.eventStore.each(function(d){
60994                 
60995                 
60996                 // FIXME..
60997                 var add =   d.data;
60998                 if (typeof(add.end_dt) == 'undefined')  {
60999                     Roo.log("Missing End time in calendar data: ");
61000                     Roo.log(d);
61001                     return;
61002                 }
61003                 if (typeof(add.start_dt) == 'undefined')  {
61004                     Roo.log("Missing Start time in calendar data: ");
61005                     Roo.log(d);
61006                     return;
61007                 }
61008                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
61009                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
61010                 add.id = add.id || d.id;
61011                 add.title = add.title || '??';
61012                 
61013                 this.addItem(d);
61014                 
61015              
61016             },this);
61017         }
61018         
61019         this.renderEvents();
61020     }
61021     
61022
61023 });
61024 /*
61025  grid : {
61026                 xtype: 'Grid',
61027                 xns: Roo.grid,
61028                 listeners : {
61029                     render : function ()
61030                     {
61031                         _this.grid = this;
61032                         
61033                         if (!this.view.el.hasClass('course-timesheet')) {
61034                             this.view.el.addClass('course-timesheet');
61035                         }
61036                         if (this.tsStyle) {
61037                             this.ds.load({});
61038                             return; 
61039                         }
61040                         Roo.log('width');
61041                         Roo.log(_this.grid.view.el.getWidth());
61042                         
61043                         
61044                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
61045                             '.course-timesheet .x-grid-row' : {
61046                                 height: '80px'
61047                             },
61048                             '.x-grid-row td' : {
61049                                 'vertical-align' : 0
61050                             },
61051                             '.course-edit-link' : {
61052                                 'color' : 'blue',
61053                                 'text-overflow' : 'ellipsis',
61054                                 'overflow' : 'hidden',
61055                                 'white-space' : 'nowrap',
61056                                 'cursor' : 'pointer'
61057                             },
61058                             '.sub-link' : {
61059                                 'color' : 'green'
61060                             },
61061                             '.de-act-sup-link' : {
61062                                 'color' : 'purple',
61063                                 'text-decoration' : 'line-through'
61064                             },
61065                             '.de-act-link' : {
61066                                 'color' : 'red',
61067                                 'text-decoration' : 'line-through'
61068                             },
61069                             '.course-timesheet .course-highlight' : {
61070                                 'border-top-style': 'dashed !important',
61071                                 'border-bottom-bottom': 'dashed !important'
61072                             },
61073                             '.course-timesheet .course-item' : {
61074                                 'font-family'   : 'tahoma, arial, helvetica',
61075                                 'font-size'     : '11px',
61076                                 'overflow'      : 'hidden',
61077                                 'padding-left'  : '10px',
61078                                 'padding-right' : '10px',
61079                                 'padding-top' : '10px' 
61080                             }
61081                             
61082                         }, Roo.id());
61083                                 this.ds.load({});
61084                     }
61085                 },
61086                 autoWidth : true,
61087                 monitorWindowResize : false,
61088                 cellrenderer : function(v,x,r)
61089                 {
61090                     return v;
61091                 },
61092                 sm : {
61093                     xtype: 'CellSelectionModel',
61094                     xns: Roo.grid
61095                 },
61096                 dataSource : {
61097                     xtype: 'Store',
61098                     xns: Roo.data,
61099                     listeners : {
61100                         beforeload : function (_self, options)
61101                         {
61102                             options.params = options.params || {};
61103                             options.params._month = _this.monthField.getValue();
61104                             options.params.limit = 9999;
61105                             options.params['sort'] = 'when_dt';    
61106                             options.params['dir'] = 'ASC';    
61107                             this.proxy.loadResponse = this.loadResponse;
61108                             Roo.log("load?");
61109                             //this.addColumns();
61110                         },
61111                         load : function (_self, records, options)
61112                         {
61113                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
61114                                 // if you click on the translation.. you can edit it...
61115                                 var el = Roo.get(this);
61116                                 var id = el.dom.getAttribute('data-id');
61117                                 var d = el.dom.getAttribute('data-date');
61118                                 var t = el.dom.getAttribute('data-time');
61119                                 //var id = this.child('span').dom.textContent;
61120                                 
61121                                 //Roo.log(this);
61122                                 Pman.Dialog.CourseCalendar.show({
61123                                     id : id,
61124                                     when_d : d,
61125                                     when_t : t,
61126                                     productitem_active : id ? 1 : 0
61127                                 }, function() {
61128                                     _this.grid.ds.load({});
61129                                 });
61130                            
61131                            });
61132                            
61133                            _this.panel.fireEvent('resize', [ '', '' ]);
61134                         }
61135                     },
61136                     loadResponse : function(o, success, response){
61137                             // this is overridden on before load..
61138                             
61139                             Roo.log("our code?");       
61140                             //Roo.log(success);
61141                             //Roo.log(response)
61142                             delete this.activeRequest;
61143                             if(!success){
61144                                 this.fireEvent("loadexception", this, o, response);
61145                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61146                                 return;
61147                             }
61148                             var result;
61149                             try {
61150                                 result = o.reader.read(response);
61151                             }catch(e){
61152                                 Roo.log("load exception?");
61153                                 this.fireEvent("loadexception", this, o, response, e);
61154                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61155                                 return;
61156                             }
61157                             Roo.log("ready...");        
61158                             // loop through result.records;
61159                             // and set this.tdate[date] = [] << array of records..
61160                             _this.tdata  = {};
61161                             Roo.each(result.records, function(r){
61162                                 //Roo.log(r.data);
61163                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
61164                                     _this.tdata[r.data.when_dt.format('j')] = [];
61165                                 }
61166                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
61167                             });
61168                             
61169                             //Roo.log(_this.tdata);
61170                             
61171                             result.records = [];
61172                             result.totalRecords = 6;
61173                     
61174                             // let's generate some duumy records for the rows.
61175                             //var st = _this.dateField.getValue();
61176                             
61177                             // work out monday..
61178                             //st = st.add(Date.DAY, -1 * st.format('w'));
61179                             
61180                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61181                             
61182                             var firstOfMonth = date.getFirstDayOfMonth();
61183                             var days = date.getDaysInMonth();
61184                             var d = 1;
61185                             var firstAdded = false;
61186                             for (var i = 0; i < result.totalRecords ; i++) {
61187                                 //var d= st.add(Date.DAY, i);
61188                                 var row = {};
61189                                 var added = 0;
61190                                 for(var w = 0 ; w < 7 ; w++){
61191                                     if(!firstAdded && firstOfMonth != w){
61192                                         continue;
61193                                     }
61194                                     if(d > days){
61195                                         continue;
61196                                     }
61197                                     firstAdded = true;
61198                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
61199                                     row['weekday'+w] = String.format(
61200                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
61201                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
61202                                                     d,
61203                                                     date.format('Y-m-')+dd
61204                                                 );
61205                                     added++;
61206                                     if(typeof(_this.tdata[d]) != 'undefined'){
61207                                         Roo.each(_this.tdata[d], function(r){
61208                                             var is_sub = '';
61209                                             var deactive = '';
61210                                             var id = r.id;
61211                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
61212                                             if(r.parent_id*1>0){
61213                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
61214                                                 id = r.parent_id;
61215                                             }
61216                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
61217                                                 deactive = 'de-act-link';
61218                                             }
61219                                             
61220                                             row['weekday'+w] += String.format(
61221                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
61222                                                     id, //0
61223                                                     r.product_id_name, //1
61224                                                     r.when_dt.format('h:ia'), //2
61225                                                     is_sub, //3
61226                                                     deactive, //4
61227                                                     desc // 5
61228                                             );
61229                                         });
61230                                     }
61231                                     d++;
61232                                 }
61233                                 
61234                                 // only do this if something added..
61235                                 if(added > 0){ 
61236                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
61237                                 }
61238                                 
61239                                 
61240                                 // push it twice. (second one with an hour..
61241                                 
61242                             }
61243                             //Roo.log(result);
61244                             this.fireEvent("load", this, o, o.request.arg);
61245                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
61246                         },
61247                     sortInfo : {field: 'when_dt', direction : 'ASC' },
61248                     proxy : {
61249                         xtype: 'HttpProxy',
61250                         xns: Roo.data,
61251                         method : 'GET',
61252                         url : baseURL + '/Roo/Shop_course.php'
61253                     },
61254                     reader : {
61255                         xtype: 'JsonReader',
61256                         xns: Roo.data,
61257                         id : 'id',
61258                         fields : [
61259                             {
61260                                 'name': 'id',
61261                                 'type': 'int'
61262                             },
61263                             {
61264                                 'name': 'when_dt',
61265                                 'type': 'string'
61266                             },
61267                             {
61268                                 'name': 'end_dt',
61269                                 'type': 'string'
61270                             },
61271                             {
61272                                 'name': 'parent_id',
61273                                 'type': 'int'
61274                             },
61275                             {
61276                                 'name': 'product_id',
61277                                 'type': 'int'
61278                             },
61279                             {
61280                                 'name': 'productitem_id',
61281                                 'type': 'int'
61282                             },
61283                             {
61284                                 'name': 'guid',
61285                                 'type': 'int'
61286                             }
61287                         ]
61288                     }
61289                 },
61290                 toolbar : {
61291                     xtype: 'Toolbar',
61292                     xns: Roo,
61293                     items : [
61294                         {
61295                             xtype: 'Button',
61296                             xns: Roo.Toolbar,
61297                             listeners : {
61298                                 click : function (_self, e)
61299                                 {
61300                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61301                                     sd.setMonth(sd.getMonth()-1);
61302                                     _this.monthField.setValue(sd.format('Y-m-d'));
61303                                     _this.grid.ds.load({});
61304                                 }
61305                             },
61306                             text : "Back"
61307                         },
61308                         {
61309                             xtype: 'Separator',
61310                             xns: Roo.Toolbar
61311                         },
61312                         {
61313                             xtype: 'MonthField',
61314                             xns: Roo.form,
61315                             listeners : {
61316                                 render : function (_self)
61317                                 {
61318                                     _this.monthField = _self;
61319                                    // _this.monthField.set  today
61320                                 },
61321                                 select : function (combo, date)
61322                                 {
61323                                     _this.grid.ds.load({});
61324                                 }
61325                             },
61326                             value : (function() { return new Date(); })()
61327                         },
61328                         {
61329                             xtype: 'Separator',
61330                             xns: Roo.Toolbar
61331                         },
61332                         {
61333                             xtype: 'TextItem',
61334                             xns: Roo.Toolbar,
61335                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
61336                         },
61337                         {
61338                             xtype: 'Fill',
61339                             xns: Roo.Toolbar
61340                         },
61341                         {
61342                             xtype: 'Button',
61343                             xns: Roo.Toolbar,
61344                             listeners : {
61345                                 click : function (_self, e)
61346                                 {
61347                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61348                                     sd.setMonth(sd.getMonth()+1);
61349                                     _this.monthField.setValue(sd.format('Y-m-d'));
61350                                     _this.grid.ds.load({});
61351                                 }
61352                             },
61353                             text : "Next"
61354                         }
61355                     ]
61356                 },
61357                  
61358             }
61359         };
61360         
61361         *//*
61362  * Based on:
61363  * Ext JS Library 1.1.1
61364  * Copyright(c) 2006-2007, Ext JS, LLC.
61365  *
61366  * Originally Released Under LGPL - original licence link has changed is not relivant.
61367  *
61368  * Fork - LGPL
61369  * <script type="text/javascript">
61370  */
61371  
61372 /**
61373  * @class Roo.LoadMask
61374  * A simple utility class for generically masking elements while loading data.  If the element being masked has
61375  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
61376  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
61377  * element's UpdateManager load indicator and will be destroyed after the initial load.
61378  * @constructor
61379  * Create a new LoadMask
61380  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
61381  * @param {Object} config The config object
61382  */
61383 Roo.LoadMask = function(el, config){
61384     this.el = Roo.get(el);
61385     Roo.apply(this, config);
61386     if(this.store){
61387         this.store.on('beforeload', this.onBeforeLoad, this);
61388         this.store.on('load', this.onLoad, this);
61389         this.store.on('loadexception', this.onLoadException, this);
61390         this.removeMask = false;
61391     }else{
61392         var um = this.el.getUpdateManager();
61393         um.showLoadIndicator = false; // disable the default indicator
61394         um.on('beforeupdate', this.onBeforeLoad, this);
61395         um.on('update', this.onLoad, this);
61396         um.on('failure', this.onLoad, this);
61397         this.removeMask = true;
61398     }
61399 };
61400
61401 Roo.LoadMask.prototype = {
61402     /**
61403      * @cfg {Boolean} removeMask
61404      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61405      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61406      */
61407     /**
61408      * @cfg {String} msg
61409      * The text to display in a centered loading message box (defaults to 'Loading...')
61410      */
61411     msg : 'Loading...',
61412     /**
61413      * @cfg {String} msgCls
61414      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61415      */
61416     msgCls : 'x-mask-loading',
61417
61418     /**
61419      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61420      * @type Boolean
61421      */
61422     disabled: false,
61423
61424     /**
61425      * Disables the mask to prevent it from being displayed
61426      */
61427     disable : function(){
61428        this.disabled = true;
61429     },
61430
61431     /**
61432      * Enables the mask so that it can be displayed
61433      */
61434     enable : function(){
61435         this.disabled = false;
61436     },
61437     
61438     onLoadException : function()
61439     {
61440         Roo.log(arguments);
61441         
61442         if (typeof(arguments[3]) != 'undefined') {
61443             Roo.MessageBox.alert("Error loading",arguments[3]);
61444         } 
61445         /*
61446         try {
61447             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61448                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61449             }   
61450         } catch(e) {
61451             
61452         }
61453         */
61454     
61455         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61456     },
61457     // private
61458     onLoad : function()
61459     {
61460         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61461     },
61462
61463     // private
61464     onBeforeLoad : function(){
61465         if(!this.disabled){
61466             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61467         }
61468     },
61469
61470     // private
61471     destroy : function(){
61472         if(this.store){
61473             this.store.un('beforeload', this.onBeforeLoad, this);
61474             this.store.un('load', this.onLoad, this);
61475             this.store.un('loadexception', this.onLoadException, this);
61476         }else{
61477             var um = this.el.getUpdateManager();
61478             um.un('beforeupdate', this.onBeforeLoad, this);
61479             um.un('update', this.onLoad, this);
61480             um.un('failure', this.onLoad, this);
61481         }
61482     }
61483 };/*
61484  * Based on:
61485  * Ext JS Library 1.1.1
61486  * Copyright(c) 2006-2007, Ext JS, LLC.
61487  *
61488  * Originally Released Under LGPL - original licence link has changed is not relivant.
61489  *
61490  * Fork - LGPL
61491  * <script type="text/javascript">
61492  */
61493
61494
61495 /**
61496  * @class Roo.XTemplate
61497  * @extends Roo.Template
61498  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61499 <pre><code>
61500 var t = new Roo.XTemplate(
61501         '&lt;select name="{name}"&gt;',
61502                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61503         '&lt;/select&gt;'
61504 );
61505  
61506 // then append, applying the master template values
61507  </code></pre>
61508  *
61509  * Supported features:
61510  *
61511  *  Tags:
61512
61513 <pre><code>
61514       {a_variable} - output encoded.
61515       {a_variable.format:("Y-m-d")} - call a method on the variable
61516       {a_variable:raw} - unencoded output
61517       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61518       {a_variable:this.method_on_template(...)} - call a method on the template object.
61519  
61520 </code></pre>
61521  *  The tpl tag:
61522 <pre><code>
61523         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61524         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61525         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61526         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61527   
61528         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61529         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61530 </code></pre>
61531  *      
61532  */
61533 Roo.XTemplate = function()
61534 {
61535     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61536     if (this.html) {
61537         this.compile();
61538     }
61539 };
61540
61541
61542 Roo.extend(Roo.XTemplate, Roo.Template, {
61543
61544     /**
61545      * The various sub templates
61546      */
61547     tpls : false,
61548     /**
61549      *
61550      * basic tag replacing syntax
61551      * WORD:WORD()
61552      *
61553      * // you can fake an object call by doing this
61554      *  x.t:(test,tesT) 
61555      * 
61556      */
61557     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61558
61559     /**
61560      * compile the template
61561      *
61562      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61563      *
61564      */
61565     compile: function()
61566     {
61567         var s = this.html;
61568      
61569         s = ['<tpl>', s, '</tpl>'].join('');
61570     
61571         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61572             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61573             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61574             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61575             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61576             m,
61577             id     = 0,
61578             tpls   = [];
61579     
61580         while(true == !!(m = s.match(re))){
61581             var forMatch   = m[0].match(nameRe),
61582                 ifMatch   = m[0].match(ifRe),
61583                 execMatch   = m[0].match(execRe),
61584                 namedMatch   = m[0].match(namedRe),
61585                 
61586                 exp  = null, 
61587                 fn   = null,
61588                 exec = null,
61589                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61590                 
61591             if (ifMatch) {
61592                 // if - puts fn into test..
61593                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61594                 if(exp){
61595                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61596                 }
61597             }
61598             
61599             if (execMatch) {
61600                 // exec - calls a function... returns empty if true is  returned.
61601                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61602                 if(exp){
61603                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61604                 }
61605             }
61606             
61607             
61608             if (name) {
61609                 // for = 
61610                 switch(name){
61611                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61612                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61613                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61614                 }
61615             }
61616             var uid = namedMatch ? namedMatch[1] : id;
61617             
61618             
61619             tpls.push({
61620                 id:     namedMatch ? namedMatch[1] : id,
61621                 target: name,
61622                 exec:   exec,
61623                 test:   fn,
61624                 body:   m[1] || ''
61625             });
61626             if (namedMatch) {
61627                 s = s.replace(m[0], '');
61628             } else { 
61629                 s = s.replace(m[0], '{xtpl'+ id + '}');
61630             }
61631             ++id;
61632         }
61633         this.tpls = [];
61634         for(var i = tpls.length-1; i >= 0; --i){
61635             this.compileTpl(tpls[i]);
61636             this.tpls[tpls[i].id] = tpls[i];
61637         }
61638         this.master = tpls[tpls.length-1];
61639         return this;
61640     },
61641     /**
61642      * same as applyTemplate, except it's done to one of the subTemplates
61643      * when using named templates, you can do:
61644      *
61645      * var str = pl.applySubTemplate('your-name', values);
61646      *
61647      * 
61648      * @param {Number} id of the template
61649      * @param {Object} values to apply to template
61650      * @param {Object} parent (normaly the instance of this object)
61651      */
61652     applySubTemplate : function(id, values, parent)
61653     {
61654         
61655         
61656         var t = this.tpls[id];
61657         
61658         
61659         try { 
61660             if(t.test && !t.test.call(this, values, parent)){
61661                 return '';
61662             }
61663         } catch(e) {
61664             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61665             Roo.log(e.toString());
61666             Roo.log(t.test);
61667             return ''
61668         }
61669         try { 
61670             
61671             if(t.exec && t.exec.call(this, values, parent)){
61672                 return '';
61673             }
61674         } catch(e) {
61675             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61676             Roo.log(e.toString());
61677             Roo.log(t.exec);
61678             return ''
61679         }
61680         try {
61681             var vs = t.target ? t.target.call(this, values, parent) : values;
61682             parent = t.target ? values : parent;
61683             if(t.target && vs instanceof Array){
61684                 var buf = [];
61685                 for(var i = 0, len = vs.length; i < len; i++){
61686                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61687                 }
61688                 return buf.join('');
61689             }
61690             return t.compiled.call(this, vs, parent);
61691         } catch (e) {
61692             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61693             Roo.log(e.toString());
61694             Roo.log(t.compiled);
61695             return '';
61696         }
61697     },
61698
61699     compileTpl : function(tpl)
61700     {
61701         var fm = Roo.util.Format;
61702         var useF = this.disableFormats !== true;
61703         var sep = Roo.isGecko ? "+" : ",";
61704         var undef = function(str) {
61705             Roo.log("Property not found :"  + str);
61706             return '';
61707         };
61708         
61709         var fn = function(m, name, format, args)
61710         {
61711             //Roo.log(arguments);
61712             args = args ? args.replace(/\\'/g,"'") : args;
61713             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61714             if (typeof(format) == 'undefined') {
61715                 format= 'htmlEncode';
61716             }
61717             if (format == 'raw' ) {
61718                 format = false;
61719             }
61720             
61721             if(name.substr(0, 4) == 'xtpl'){
61722                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61723             }
61724             
61725             // build an array of options to determine if value is undefined..
61726             
61727             // basically get 'xxxx.yyyy' then do
61728             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61729             //    (function () { Roo.log("Property not found"); return ''; })() :
61730             //    ......
61731             
61732             var udef_ar = [];
61733             var lookfor = '';
61734             Roo.each(name.split('.'), function(st) {
61735                 lookfor += (lookfor.length ? '.': '') + st;
61736                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
61737             });
61738             
61739             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
61740             
61741             
61742             if(format && useF){
61743                 
61744                 args = args ? ',' + args : "";
61745                  
61746                 if(format.substr(0, 5) != "this."){
61747                     format = "fm." + format + '(';
61748                 }else{
61749                     format = 'this.call("'+ format.substr(5) + '", ';
61750                     args = ", values";
61751                 }
61752                 
61753                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61754             }
61755              
61756             if (args.length) {
61757                 // called with xxyx.yuu:(test,test)
61758                 // change to ()
61759                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61760             }
61761             // raw.. - :raw modifier..
61762             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61763             
61764         };
61765         var body;
61766         // branched to use + in gecko and [].join() in others
61767         if(Roo.isGecko){
61768             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61769                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61770                     "';};};";
61771         }else{
61772             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61773             body.push(tpl.body.replace(/(\r\n|\n)/g,
61774                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61775             body.push("'].join('');};};");
61776             body = body.join('');
61777         }
61778         
61779         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61780        
61781         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61782         eval(body);
61783         
61784         return this;
61785     },
61786
61787     applyTemplate : function(values){
61788         return this.master.compiled.call(this, values, {});
61789         //var s = this.subs;
61790     },
61791
61792     apply : function(){
61793         return this.applyTemplate.apply(this, arguments);
61794     }
61795
61796  });
61797
61798 Roo.XTemplate.from = function(el){
61799     el = Roo.getDom(el);
61800     return new Roo.XTemplate(el.value || el.innerHTML);
61801 };